· 9 years ago · Nov 08, 2016, 02:54 PM
1/*
2s_dsp.c - digital signal processing algorithms for audio FX
3Copyright (C) 2009 Uncle Mike
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14*/
15
16#include "quakedef.h"
17#include "sound.h"
18
19//dr_mabuse1981: move this out from here when it works!!
20
21#define MAX_RANDOM_RANGE 0x7FFFFFFFUL
22#define IA 16807
23#define IM 2147483647
24#define IQ 127773
25#define IR 2836
26#define NTAB 32
27#define NDIV (1+(IM-1)/NTAB)
28#define AM (1.0/IM)
29#define EPS 1.2e-7
30#define RNMX (1.0 - EPS)
31#define max(a, b) (a) > (b) ? (a) : (b)
32#define min(a, b) (a) > (b) ? (b) : (a)
33#define ASSERT( exp ) if(!( exp )) Sys_Error( "assert failed at %s:%i\n", __FILE__, __LINE__ )
34
35 static long idum = 0;
36 typedef void* HANDLE;
37 typedef unsigned int dword;
38
39void COM_SetRandomSeed( long lSeed )
40{
41 if( lSeed ) idum = lSeed;
42 else idum = -time( NULL );
43
44 if( 1000 < idum )
45 idum = -idum;
46 else if( -1000 < idum )
47 idum -= 22261048;
48}
49
50long Com_RandomLong( long lLow, long lHigh )
51{
52 dword maxAcceptable;
53 dword n, x = lHigh-lLow + 1;
54
55 if( idum == 0 ) COM_SetRandomSeed(0);
56
57 if( x <= 0 || MAX_RANDOM_RANGE < x-1 )
58 return lLow;
59
60 // The following maps a uniform distribution on the interval [0, MAX_RANDOM_RANGE]
61 // to a smaller, client-specified range of [0,x-1] in a way that doesn't bias
62 // the uniform distribution unfavorably. Even for a worst case x, the loop is
63 // guaranteed to be taken no more than half the time, so for that worst case x,
64 // the average number of times through the loop is 2. For cases where x is
65 // much smaller than MAX_RANDOM_RANGE, the average number of times through the
66 // loop is very close to 1.
67 maxAcceptable = MAX_RANDOM_RANGE - ((MAX_RANDOM_RANGE+1) % x );
68 do
69 {
70 n = lran1();
71 } while( n > maxAcceptable );
72
73 return lLow + (n % x);
74}
75
76#ifndef M_PI
77#define M_PI (float)3.14159265358979323846
78#endif
79
80#ifndef M_PI2
81#define M_PI2 (float)6.28318530717958647692
82#endif
83
84#define M_PI_F ((float)(M_PI))
85#define M_PI2_F ((float)(M_PI2))
86
87#define SOUND_DMA_SPEED 22050 // hardware playback rate
88#define SIGN( d ) (( d ) < 0 ? -1 : 1 )
89#define ABS( a ) abs( a )
90#define MSEC_TO_SAMPS( a ) ((( a ) * SOUND_DMA_SPEED) / 1000 ) // convert milliseconds to # samples in equivalent time
91#define SEC_TO_SAMPS( a ) (( a ) * SOUND_DMA_SPEED) // conver seconds to # samples in equivalent time
92#define CLIP_DSP( x ) ( x )
93#define SOUND_MS_PER_FT 1 // sound travels approx 1 foot per millisecond
94#define ROOM_MAX_SIZE 1000 // max size in feet of room simulation for dsp
95
96// Performance notes:
97
98// DSP processing should take no more than 3ms total time per frame to remain on par with hl1
99// Assume a min frame rate of 24fps = 42ms per frame
100// at 24fps, to maintain 44.1khz output rate, we must process about 1840 mono samples per frame.
101// So we must process 1840 samples in 3ms.
102
103// on a 1Ghz CPU (mid-low end CPU) 3ms provides roughly 3,000,000 cycles.
104// Thus we have 3e6 / 1840 = 1630 cycles per sample.
105
106#define PBITS 12 // parameter bits
107#define PMAX ((1 << PBITS)-1) // parameter max size
108
109// crossfade from y2 to y1 at point r (0 < r < PMAX )
110#define XFADE( y1, y2, r ) (((y1) * (r)) >> PBITS) + (((y2) * (PMAX - (r))) >> PBITS);
111#define XFADEF( y1, y2, r ) (((y1) * (r)) / (float)(PMAX)) + (((y2) * (PMAX - (r))) / (float)(PMAX));
112
113/////////////////////
114// dsp helpers
115/////////////////////
116
117// dot two integer vectors of length M+1
118// M is filter order, h is filter vector, w is filter state vector
119inline int dot ( int M, int *h, int *w )
120{
121 int i, y;
122
123 for( y = 0, i = 0; i <= M; i++ )
124 y += ( h[i] * w[i] ) >> PBITS;
125 return y;
126}
127
128// delay array w[] by D samples
129// w[0] = input, w[D] = output
130// practical for filters, but not for large values of D
131inline void delay( int D, int *w )
132{
133 int i;
134
135 for( i = D; i >= 1; i-- ) // reverse order updating
136 w[i] = w[i-1];
137}
138
139// circular wrap of pointer p, relative to array w
140// D delay line size in samples w[0...D]
141// w delay line buffer pointer, dimension D+1
142// p circular pointer
143inline void wrap( int D, int *w, int **p )
144{
145 if( *p > w + D ) *p -= D + 1; // when *p = w + D + 1, it wraps around to *p = w
146 if( *p < w ) *p += D + 1; // when *p = w - 1, it wraps around to *p = w + D
147}
148
149// simple averaging filter for performance - a[] is 0, b[] is 1, L is # of samples to average
150inline int avg_filter( int M, int *a, int L, int *b, int *w, int x )
151{
152 int i, y = 0;
153
154 w[0] = x;
155
156 // output adder
157 switch( L )
158 {
159 default:
160 case 12: y += w[12];
161 case 11: y += w[11];
162 case 10: y += w[10];
163 case 9: y += w[9];
164 case 8: y += w[8];
165 case 7: y += w[7];
166 case 6: y += w[6];
167 case 5: y += w[5];
168 case 4: y += w[4];
169 case 3: y += w[3];
170 case 2: y += w[2];
171 case 1: y += w[1];
172 case 0: y += w[0];
173 }
174
175 for( i = L; i >= 1; i-- ) // reverse update internal state
176 w[i] = w[i-1];
177
178 switch( L )
179 {
180 default:
181 case 12: return y / 13;
182 case 11: return y / 12;
183 case 10: return y / 11;
184 case 9: return y / 10;
185 case 8: return y / 9;
186 case 7: return y >> 3;
187 case 6: return y / 7;
188 case 5: return y / 6;
189 case 4: return y / 5;
190 case 3: return y >> 2;
191 case 2: return y / 3;
192 case 1: return y >> 1;
193 case 0: return y;
194 }
195}
196
197// IIR filter, cannonical form
198// returns single sample y for current input value x
199// x is input sample
200// w = internal state vector, dimension max(M,L) + 1
201// L, M numerator and denominator filter orders
202// a,b are M+1 dimensional arrays of filter params
203//
204// for M = 4:
205//
206// 1 w0(n) b0
207// x(n)--->(+)--(*)-----.------(*)->(+)---> y(n)
208// ^ | ^
209// | [Delay d] |
210// | | |
211// | -a1 |W1 b1 |
212// ----(*)---.------(*)----
213// ^ | ^
214// | [Delay d] |
215// | | |
216// | -a2 |W2 b2 |
217// ----(*)---.------(*)----
218// ^ | ^
219// | [Delay d] |
220// | | |
221// | -a3 |W3 b3 |
222// ----(*)---.------(*)----
223// ^ | ^
224// | [Delay d] |
225// | | |
226// | -a4 |W4 b4 |
227// ----(*)---.------(*)----
228//
229// for each input sample x, do:
230// w0 = x - a1*w1 - a2*w2 - ... aMwM
231// y = b0*w0 + b1*w1 + ...bL*wL
232// wi = wi-1, i = K, K-1, ..., 1
233
234inline int iir_filter( int M, int *a, int L, int *b, int *w, int x )
235{
236 int K, i, y, x0;
237
238 if( M == 0 )
239 return avg_filter( M, a, L, b, w, x );
240
241 y = 0;
242 x0 = x;
243
244 K = max ( M, L );
245
246 // for (i = 1; i <= M; i++) // input adder
247 // w[0] -= ( a[i] * w[i] ) >> PBITS;
248
249 // M is clamped between 1 and FLT_M
250 // change this switch statement if FLT_M changes!
251
252 switch( M )
253 {
254 case 12: x0 -= ( a[12] * w[12] ) >> PBITS;
255 case 11: x0 -= ( a[11] * w[11] ) >> PBITS;
256 case 10: x0 -= ( a[10] * w[10] ) >> PBITS;
257 case 9: x0 -= ( a[9] * w[9] ) >> PBITS;
258 case 8: x0 -= ( a[8] * w[8] ) >> PBITS;
259 case 7: x0 -= ( a[7] * w[7] ) >> PBITS;
260 case 6: x0 -= ( a[6] * w[6] ) >> PBITS;
261 case 5: x0 -= ( a[5] * w[5] ) >> PBITS;
262 case 4: x0 -= ( a[4] * w[4] ) >> PBITS;
263 case 3: x0 -= ( a[3] * w[3] ) >> PBITS;
264 case 2: x0 -= ( a[2] * w[2] ) >> PBITS;
265 default:
266 case 1: x0 -= ( a[1] * w[1] ) >> PBITS;
267 }
268
269 w[0] = x0;
270
271 // for( i = 0; i <= L; i++ ) // output adder
272 // y += ( b[i] * w[i] ) >> PBITS;
273
274 switch( L )
275 {
276 case 12: y += ( b[12] * w[12] ) >> PBITS;
277 case 11: y += ( b[11] * w[11] ) >> PBITS;
278 case 10: y += ( b[10] * w[10] ) >> PBITS;
279 case 9: y += ( b[9] * w[9] ) >> PBITS;
280 case 8: y += ( b[8] * w[8] ) >> PBITS;
281 case 7: y += ( b[7] * w[7] ) >> PBITS;
282 case 6: y += ( b[6] * w[6] ) >> PBITS;
283 case 5: y += ( b[5] * w[5] ) >> PBITS;
284 case 4: y += ( b[4] * w[4] ) >> PBITS;
285 case 3: y += ( b[3] * w[3] ) >> PBITS;
286 case 2: y += ( b[2] * w[2] ) >> PBITS;
287 default:
288 case 1: y += ( b[1] * w[1] ) >> PBITS;
289 case 0: y += ( b[0] * w[0] ) >> PBITS;
290 }
291
292 for( i = K; i >= 1; i-- ) // reverse update internal state
293 w[i] = w[i-1];
294
295 return y; // current output sample
296}
297
298// IIR filter, cannonical form, using dot product and delay implementation
299// (may be easier to optimize this routine.)
300inline int iir_filter2( int M, int *a, int L, int *b, int *w, int x )
301{
302 int K, y;
303
304 K = max( M, L ); // K = max (M, L)
305 w[0] = 0; // needed for dot (M, a, w)
306
307 w[0] = x - dot( M, a, w ); // input adder
308 y = dot( L, b, w ); // output adder
309
310 delay( K, w ); // update delay line
311
312 return y; // current output sample
313}
314
315
316// fir filter - no feedback = high stability but also may be more expensive computationally
317inline int fir_filter( int M, int *h, int *w, int x )
318{
319 int i, y;
320
321 w[0] = x;
322
323 for( y = 0, i = 0; i <= M; i++ )
324 y += h[i] * w[i];
325
326 for( i = M; i >= -1; i-- )
327 w[i] = w[i-1];
328
329 return y;
330}
331
332// fir filter, using dot product and delay implementation
333inline int fir_filter2( int M, int *h, int *w, int x )
334{
335 int y;
336
337 w[0] = x;
338 y = dot( M, h, w );
339 delay( M, w );
340
341 return y;
342}
343
344
345// tap - i-th tap of circular delay line buffer
346// D delay line size in samples
347// w delay line buffer pointer, of dimension D+1
348// p circular pointer
349// t = 0...D
350int tap( int D, int *w, int *p, int t )
351{
352 return w[(p - w + t) % (D + 1)];
353}
354
355// tapi - interpolated tap output of a delay line
356// interpolates sample between adjacent samples in delay line for 'frac' part of delay
357// D delay line size in samples
358// w delay line buffer pointer, of dimension D+1
359// p circular pointer
360// t - delay tap integer value 0...D. (complete delay is t.frac )
361// frac - varying 16 bit fractional delay value 0...32767 (normalized to 0.0 - 1.0)
362inline int tapi( int D, int *w, int *p, int t, int frac )
363{
364 int i, j;
365 int si, sj;
366
367 i = t; // tap value, interpolate between adjacent samples si and sj
368 j = (i + 1) % (D+1); // if i = D, then j = 0; otherwise, j = i + 1
369
370 si = tap( D, w, p, i ); // si(n) = x(n - i)
371 sj = tap( D, w, p, j ); // sj(n) = x(n - j)
372
373 return si + (((frac) * (sj - si) ) >> 16);
374}
375
376// circular delay line, D-fold delay
377// D delay line size in samples w[0..D]
378// w delay line buffer pointer, dimension D+1
379// p circular pointer
380inline void cdelay( int D, int *w, int **p )
381{
382 (*p)--; // decrement pointer and wrap modulo (D+1)
383 wrap ( D, w, p ); // when *p = w-1, it wraps around to *p = w+D
384}
385
386// plain reverberator with circular delay line
387// D delay line size in samples
388// t tap from this location - <= D
389// w delay line buffer pointer of dimension D+1
390// p circular pointer, must be init to &w[0] before first call
391// a feedback value, 0-PMAX (normalized to 0.0-1.0)
392// b gain
393// x input sample
394
395// w0(n) b
396// x(n)--->(+)--------.-----(*)-> y(n)
397// ^ |
398// | [Delay d]
399// | |
400// | a |Wd(n)
401// ----(*)---.
402
403inline int dly_plain( int D, int t, int *w, int **p, int a, int b, int x )
404{
405 int y, sD;
406
407 sD = tap( D, w, *p, t ); // Tth tap delay output
408 y = x + (( a * sD ) >> PBITS); // filter output
409 **p = y; // delay input
410 cdelay( D, w, p ); // update delay line
411
412 return (( y * b ) >> PBITS );
413}
414
415// straight delay line
416//
417// D delay line size in samples
418// t tap from this location - <= D
419// w delay line buffer pointer of dimension D+1
420// p circular pointer, must be init to &w[0] before first call
421// x input sample
422//
423// x(n)--->[Delay d]---> y(n)
424//
425inline int dly_linear ( int D, int t, int *w, int **p, int x )
426{
427 int y;
428
429 y = tap( D, w, *p, t ); // Tth tap delay output
430 **p = x; // delay input
431 cdelay( D, w, p ); // update delay line
432
433 return y;
434}
435
436// lowpass reverberator, replace feedback multiplier 'a' in
437// plain reverberator with a low pass filter
438// D delay line size in samples
439// t tap from this location - <= D
440// w delay line buffer pointer of dimension D+1
441// p circular pointer, must be init to &w[0] before first call
442// a feedback gain
443// b output gain
444// M filter order
445// bf filter numerator, 0-PMAX (normalized to 0.0-1.0), M+1 dimensional
446// af filter denominator, 0-PMAX (normalized to 0.0-1.0), M+1 dimensional
447// vf filter state, M+1 dimensional
448// x input sample
449// w0(n) b
450// x(n)--->(+)--------------.----(*)--> y(n)
451// ^ |
452// | [Delay d]
453// | |
454// | a |Wd(n)
455// --(*)--[Filter])-
456
457int dly_lowpass( int D, int t, int *w, int **p, int a, int b, int M, int *af, int L, int *bf, int *vf, int x )
458{
459 int y, sD;
460
461 sD = tap( D, w, *p, t ); // delay output is filter input
462 y = x + ((iir_filter ( M, af, L, bf, vf, sD ) * a) >> PBITS); // filter output with gain
463 **p = y; // delay input
464 cdelay( D, w, p ); // update delay line
465
466 return (( y * b ) >> PBITS ); // output with gain
467}
468
469// allpass reverberator with circular delay line
470// D delay line size in samples
471// t tap from this location - <= D
472// w delay line buffer pointer of dimension D+1
473// p circular pointer, must be init to &w[0] before first call
474// a feedback value, 0-PMAX (normalized to 0.0-1.0)
475// b gain
476
477// w0(n) -a b
478// x(n)--->(+)--------.-----(*)-->(+)--(*)-> y(n)
479// ^ | ^
480// | [Delay d] |
481// | | |
482// | a |Wd(n) |
483// ----(*)---.-------------
484//
485// for each input sample x, do:
486// w0 = x + a*Wd
487// y = -a*w0 + Wd
488// delay (d, W) - w is the delay buffer array
489//
490// or, using circular delay, for each input sample x do:
491//
492// Sd = tap (D,w,p,D)
493// S0 = x + a*Sd
494// y = -a*S0 + Sd
495// *p = S0
496// cdelay(D, w, &p)
497
498inline int dly_allpass( int D, int t, int *w, int **p, int a, int b, int x )
499{
500 int y, s0, sD;
501
502 sD = tap( D, w, *p, t ); // Dth tap delay output
503 s0 = x + (( a * sD ) >> PBITS);
504
505 y = (( -a * s0 ) >> PBITS ) + sD; // filter output
506 **p = s0; // delay input
507 cdelay( D, w, p ); // update delay line
508
509 return (( y * b ) >> PBITS );
510}
511
512
513///////////////////////////////////////////////////////////////////////////////////
514// fixed point math for real-time wave table traversing, pitch shifting, resampling
515///////////////////////////////////////////////////////////////////////////////////
516#define FIX20_BITS 20 // 20 bits of fractional part
517#define FIX20_SCALE (1 << FIX20_BITS)
518#define FIX20_INTMAX ((1 << (32 - FIX20_BITS))-1) // maximum step integer
519#define FLOAT_TO_FIX20(a) ((int)((a) * (float)FIX20_SCALE)) // convert float to fixed point
520#define INT_TO_FIX20(a) (((int)(a)) << FIX20_BITS) // convert int to fixed point
521#define FIX20_TO_FLOAT(a) ((float)(a) / (float)FIX20_SCALE) // convert fix20 to float
522#define FIX20_INTPART(a) (((int)(a)) >> FIX20_BITS) // get integer part of fixed point
523#define FIX20_FRACPART(a) ((a) - (((a) >> FIX20_BITS) << FIX20_BITS)) // get fractional part of fixed point
524#define FIX20_FRACTION(a,b) (FIX(a)/(b)) // convert int a to fixed point, divide by b
525
526typedef int fix20int;
527
528/////////////////////////////////
529// DSP processor parameter block
530/////////////////////////////////
531
532// NOTE: these prototypes must match the XXX_Params ( prc_t *pprc ) and XXX_GetNext ( XXX_t *p, int x ) functions
533
534typedef void * (*prc_Param_t)( void *pprc ); // individual processor allocation functions
535typedef int (*prc_GetNext_t)( void *pdata, int x ); // get next function for processor
536typedef int (*prc_GetNextN_t)( void *pdata, portable_samplepair_t *pbuffer, int SampleCount, int op); // batch version of getnext
537typedef void (*prc_Free_t)( void *pdata ); // free function for processor
538typedef void (*prc_Mod_t)(void *pdata, float v); // modulation function for processor
539
540#define OP_LEFT 0 // batch process left channel in place
541#define OP_RIGHT 1 // batch process right channel in place
542#define OP_LEFT_DUPLICATE 2 // batch process left channel in place, duplicate to right channel
543
544#define PRC_NULL 0 // pass through - must be 0
545#define PRC_DLY 1 // simple feedback reverb
546#define PRC_RVA 2 // parallel reverbs
547#define PRC_FLT 3 // lowpass or highpass filter
548#define PRC_CRS 4 // chorus
549#define PRC_PTC 5 // pitch shifter
550#define PRC_ENV 6 // adsr envelope
551#define PRC_LFO 7 // lfo
552#define PRC_EFO 8 // envelope follower
553#define PRC_MDY 9 // mod delay
554#define PRC_DFR 10 // diffusor - n series allpass delays
555#define PRC_AMP 11 // amplifier with distortion
556
557#define QUA_LO 0 // quality of filter or reverb. Must be 0,1,2,3.
558#define QUA_MED 1
559#define QUA_HI 2
560#define QUA_VHI 3
561#define QUA_MAX QUA_VHI
562
563#define CPRCPARAMS 16 // up to 16 floating point params for each processor type
564
565// processor definition - one for each running instance of a dsp processor
566typedef struct
567{
568 int type; // PRC type
569
570 float prm[CPRCPARAMS]; // dsp processor parameters - array of floats
571
572 prc_Param_t pfnParam; // allocation function - takes ptr to prc, returns ptr to specialized data struct for proc type
573 prc_GetNext_t pfnGetNext; // get next function
574 prc_GetNextN_t pfnGetNextN; // batch version of get next
575 prc_Free_t pfnFree; // free function
576 prc_Mod_t pfnMod; // modulation function
577
578 void *pdata; // processor state data - ie: pdly, pflt etc.
579} prc_t;
580
581// processor parameter ranges - for validating parameters during allocation of new processor
582typedef struct prm_rng_s
583{
584 int iprm; // parameter index
585 float lo; // min value of parameter
586 float hi; // max value of parameter
587} prm_rng_t;
588
589void PRC_CheckParams( prc_t *pprc, prm_rng_t *prng );
590
591///////////
592// Filters
593///////////
594
595#define CFLTS 64 // max number of filters simultaneously active
596#define FLT_M 12 // max order of any filter
597
598#define FLT_LP 0 // lowpass filter
599#define FLT_HP 1 // highpass filter
600#define FTR_MAX FLT_HP
601
602// flt parameters
603
604typedef struct
605{
606 qboolean fused; // true if slot in use
607
608 int b[FLT_M+1]; // filter numerator parameters (convert 0.0-1.0 to 0-PMAX representation)
609 int a[FLT_M+1]; // filter denominator parameters (convert 0.0-1.0 to 0-PMAX representation)
610 int w[FLT_M+1]; // filter state - samples (dimension of max (M, L))
611 int L; // filter order numerator (dimension of a[M+1])
612 int M; // filter order denominator (dimension of b[L+1])
613} flt_t;
614
615// flt flts
616flt_t flts[CFLTS];
617
618void FLT_Init( flt_t *pf ) { if( pf ) Q_memset( pf, 0, sizeof( flt_t )); }
619void FLT_InitAll( void ) { int i; for( i = 0; i < CFLTS; i++ ) FLT_Init( &flts[i] ); }
620void FLT_Free( flt_t *pf ) { if( pf ) Q_memset( pf, 0, sizeof( flt_t )); }
621void FLT_FreeAll( void ) { int i; for( i = 0; i < CFLTS; i++ ) FLT_Free( &flts[i] ); }
622
623
624// find a free filter from the filter pool
625// initialize filter numerator, denominator b[0..M], a[0..L]
626flt_t * FLT_Alloc( int M, int L, int *a, int *b )
627{
628 int i, j;
629 flt_t *pf = NULL;
630
631 for( i = 0; i < CFLTS; i++ )
632 {
633 if( !flts[i].fused )
634 {
635 pf = &flts[i];
636
637 // transfer filter params into filter struct
638 pf->M = M;
639 pf->L = L;
640 for( j = 0; j <= M; j++ )
641 pf->a[j] = a[j];
642
643 for( j = 0; j <= L; j++ )
644 pf->b[j] = b[j];
645
646 pf->fused = true;
647 break;
648 }
649 }
650
651 ASSERT( pf ); // make sure we're not trying to alloc more than CFLTS flts
652
653 return pf;
654}
655
656// convert filter params cutoff and type into
657// iir transfer function params M, L, a[], b[]
658// iir filter, 1st order, transfer function is H(z) = b0 + b1 Z^-1 / a0 + a1 Z^-1
659// or H(z) = b0 - b1 Z^-1 / a0 + a1 Z^-1 for lowpass
660// design cutoff filter at 3db (.5 gain) p579
661void FLT_Design_3db_IIR( float cutoff, float ftype, int *pM, int *pL, int *a, int *b )
662{
663 // ftype: FLT_LP, FLT_HP, FLT_BP
664
665 double Wc = M_PI2 * cutoff / SOUND_DMA_SPEED; // radians per sample
666 double Oc;
667 double fa;
668 double fb;
669
670 // calculations:
671 // Wc = 2pi * fc/44100 convert to radians
672 // Oc = tan (Wc/2) * Gc / sqt ( 1 - Gc^2) get analog version, low pass
673 // Oc = tan (Wc/2) * (sqt (1 - Gc^2)) / Gc analog version, high pass
674 // Gc = 10 ^ (-Ac/20) gain at cutoff. Ac = 3db, so Gc^2 = 0.5
675 // a = ( 1 - Oc ) / ( 1 + Oc )
676 // b = ( 1 - a ) / 2
677
678 Oc = tan( Wc / 2.0 );
679
680 fa = ( 1.0 - Oc ) / ( 1.0 + Oc );
681
682 fb = ( 1.0 - fa ) / 2.0;
683
684 if( ftype == FLT_HP )
685 fb = ( 1.0 + fa ) / 2.0;
686
687 a[0] = 0; // a0 always ignored
688 a[1] = (int)( -fa * PMAX ); // quantize params down to 0-PMAX >> PBITS
689 b[0] = (int)( fb * PMAX );
690 b[1] = b[0];
691
692 if( ftype == FLT_HP )
693 b[1] = -b[1];
694
695 *pM = *pL = 1;
696}
697
698
699// convolution of x[n] with h[n], resulting in y[n]
700// h, x, y filter, input and output arrays (double precision)
701// M = filter order, L = input length
702// h is M+1 dimensional
703// x is L dimensional
704// y is L+M dimensional
705void conv( int M, double *h, int L, double *x, double *y )
706{
707 int n, m;
708
709 for( n = 0; n < L+M; n++ )
710 {
711 for( y[n] = 0, m = max(0, n-L+1); m <= min(n, M); m++ )
712 {
713 y[n] += h[m] * x[n-m];
714 }
715 }
716}
717
718// cas2can - convert cascaded, second order section parameter arrays to
719// canonical numerator/denominator arrays. Canonical implementations
720// have half as many multiplies as cascaded implementations.
721
722// K is number of cascaded sections
723// A is Kx3 matrix of sos params A[K] = A[0]..A[K-1]
724// a is (2K + 1) -dimensional output of canonical params
725
726#define KMAX 32 // max # of sos sections - 8 is the most we should ever see at runtime
727
728void cas2can( int K, double A[KMAX+1][3], int *aout )
729{
730 int i, j;
731 double d[2*KMAX + 1];
732 double a[2*KMAX + 1];
733
734 ASSERT( K <= KMAX );
735
736 Q_memset( d, 0, sizeof( double ) * ( 2 * KMAX + 1 ));
737 Q_memset( a, 0, sizeof( double ) * ( 2 * KMAX + 1 ));
738
739 a[0] = 1;
740
741 for( i = 0; i < K; i++ )
742 {
743 conv( 2, A[i], 2 * i + 1, a, d );
744
745 for( j = 0; j < 2 * i + 3; j++ )
746 a[j] = d[j];
747 }
748
749 for( i = 0; i < (2*K + 1); i++ )
750 aout[i] = a[i] * PMAX;
751}
752
753
754// chebyshev IIR design, type 2, Lowpass or Highpass
755
756#define lnf( e ) ( 2.303 * log10( e ))
757#define acosh( e ) ( lnf( (e) + sqrt(( e ) * ( e ) - 1) ))
758#define asinh( e ) ( lnf( (e) + sqrt(( e ) * ( e ) + 1) ))
759
760
761// returns a[], b[] which are Kx3 matrices of cascaded second-order sections
762// these matrices may be passed directly to the iir_cas() routine for evaluation
763// Nmax - maximum order of filter
764// cutoff, ftype, qwidth - filter cutoff in hz, filter type FLT_LOWPASS/HIGHPASS, qwidth in hz
765// pM - denominator order
766// pL - numerator order
767// a - array of canonical filter params
768// b - array of canonical filter params
769void FLT_Design_Cheb( int Nmax, float cutoff, float ftype, float qwidth, int *pM, int *pL, int *a, int *b )
770{
771// p769 - converted from MATLAB
772
773 double s = (ftype == FLT_LP ? 1 : -1 ); // 1 for LP, -1 for HP
774 double fs = SOUND_DMA_SPEED; // sampling frequency
775 double fpass = cutoff; // cutoff frequency
776 double fstop = fpass + max (2000, qwidth); // stop frequency
777 double Apass = 0.5; // max attenuation of pass band UNDONE: use Quality to select this
778 double Astop = 10; // max amplitude of stop band UNDONE: use Quality to select this
779
780 double Wpass, Wstop, epass, estop, Nex, aa;
781 double W3, f3, W0, G, Wi2, W02, a1, a2, th, Wi, D, b1;
782 int i, K, r, N;
783 double A[KMAX+1][3]; // denominator output matrices, second order sections
784 double B[KMAX+1][3]; // numerator output matrices, second order sections
785
786 Wpass = tan( M_PI * fpass / fs );
787 Wpass = pow( Wpass, s );
788 Wstop = tan( M_PI * fstop / fs );
789 Wstop = pow( Wstop, s );
790
791 epass = sqrt( pow( (float)10.0f, (float)Apass/10.0f ) - 1 );
792 estop = sqrt( pow( (float)10.0f, (float)Astop/10.0f ) - 1 );
793
794 // calculate filter order N
795
796 Nex = acosh( estop/epass ) / acosh ( Wstop/Wpass );
797 N = min ( ceil(Nex), Nmax ); // don't exceed Nmax for filter order
798 r = ( (int)N & 1); // r == 1 if N is odd
799 K = (N - r ) / 2;
800
801 aa = asinh ( estop ) / N;
802 W3 = Wstop / cosh( acosh( estop ) / N );
803 f3 = (fs / M_PI) * atan( pow( W3, s ));
804
805 W0 = sinh( aa ) / Wstop;
806 W02 = W0 * W0;
807
808 // 1st order section for N odd
809 if( r == 1 )
810 {
811 G = 1 / (1 + W0);
812 A[0][0] = 1; A[0][1] = s * (2*G-1); A[0][2] = 0;
813 B[0][0] = G; B[0][1] = G * s; B[0][2] = 0;
814 }
815 else
816 {
817 A[0][0] = 1; A[0][1] = 0; A[0][2] = 0;
818 B[0][0] = 1; B[0][1] = 0; B[0][2] = 0;
819 }
820
821 for( i = 1; i <= K ; i++ )
822 {
823 th = M_PI * (N - 1 + 2 * i) / (2 * N);
824 Wi = sin( th ) / Wstop;
825 Wi2 = Wi * Wi;
826
827 D = 1 - 2 * W0 * cos( th ) + W02 + Wi2;
828 G = ( 1 + Wi2 ) / D;
829
830 b1 = 2 * ( 1 - Wi2 ) / ( 1 + Wi2 );
831 a1 = 2 * ( 1 - W02 - Wi2) / D;
832 a2 = ( 1 + 2 * W0 * cos( th ) + W02 + Wi2) / D;
833
834 A[i][0] = 1;
835 A[i][1] = s * a1;
836 A[i][2] = a2;
837
838 B[i][0] = G;
839 B[i][1] = G* s* b1;
840 B[i][2] = G;
841 }
842
843 // convert cascade parameters to canonical parameters
844
845 cas2can( K, A, a );
846 *pM = 2*K + 1;
847
848 cas2can( K, B, b );
849 *pL = 2*K + 1;
850}
851
852// filter parameter order
853
854typedef enum
855{
856 flt_iftype,
857 flt_icutoff,
858 flt_iqwidth,
859 flt_iquality,
860
861 flt_cparam // # of params
862} flt_e;
863
864// filter parameter ranges
865
866prm_rng_t flt_rng[] =
867{
868{ flt_cparam, 0, 0 }, // first entry is # of parameters
869{ flt_iftype, 0, FTR_MAX }, // filter type FLT_LP, FLT_HP, FLT_BP (UNDONE: FLT_BP currently ignored)
870{ flt_icutoff, 10, 22050 }, // cutoff frequency in hz at -3db gain
871{ flt_iqwidth, 100, 11025 }, // width of BP, or steepness of LP/HP (ie: fcutoff + qwidth = -60db gain point)
872{ flt_iquality, 0, QUA_MAX }, // QUA_LO, _MED, _HI 0,1,2,3
873};
874
875
876// convert prc float params to iir filter params, alloc filter and return ptr to it
877// filter quality set by prc quality - 0,1,2
878flt_t * FLT_Params ( prc_t *pprc )
879{
880 float qual = pprc->prm[flt_iquality];
881 float cutoff = pprc->prm[flt_icutoff];
882 float ftype = pprc->prm[flt_iftype];
883 float qwidth = pprc->prm[flt_iqwidth];
884
885 int L = 0; // numerator order
886 int M = 0; // denominator order
887 int b[FLT_M+1]; // numerator params 0..PMAX
888 int a[FLT_M+1]; // denominator params 0..PMAX
889
890 // low pass and highpass filter design
891
892 if( (int)qual == QUA_LO )
893 qual = QUA_MED; // disable lowest quality filter - check perf on lowend KDB
894
895 switch ( (int)qual )
896 {
897 case QUA_LO:
898 // lowpass averaging filter: perf KDB
899 ASSERT( ftype == FLT_LP );
900 ASSERT( cutoff <= SOUND_DMA_SPEED );
901 M = 0;
902
903 // L is # of samples to average
904
905 L = 0;
906 if( cutoff <= SOUND_DMA_SPEED / 4 ) L = 1; // 11k
907 if( cutoff <= SOUND_DMA_SPEED / 8 ) L = 2; // 5.5k
908 if( cutoff <= SOUND_DMA_SPEED / 16 ) L = 4; // 2.75k
909 if( cutoff <= SOUND_DMA_SPEED / 32 ) L = 8; // 1.35k
910 if( cutoff <= SOUND_DMA_SPEED / 64 ) L = 12; // 750hz
911
912 break;
913 case QUA_MED:
914 // 1st order IIR filter, 3db cutoff at fc
915 FLT_Design_3db_IIR( cutoff, ftype, &M, &L, a, b );
916
917 M = bound( 1, M, FLT_M );
918 L = bound( 1, L, FLT_M );
919 break;
920 case QUA_HI:
921 // type 2 chebyshev N = 4 IIR
922 FLT_Design_Cheb( 4, cutoff, ftype, qwidth, &M, &L, a, b );
923
924 M = bound( 1, M, FLT_M );
925 L = bound( 1, L, FLT_M );
926 break;
927 case QUA_VHI:
928 // type 2 chebyshev N = 7 IIR
929 FLT_Design_Cheb( 8, cutoff, ftype, qwidth, &M, &L, a, b );
930
931 M = bound( 1, M, FLT_M );
932 L = bound( 1, L, FLT_M );
933 break;
934 }
935
936 return FLT_Alloc( M, L, a, b );
937}
938
939inline void * FLT_VParams( void *p )
940{
941 PRC_CheckParams(( prc_t *)p, flt_rng );
942 return (void *)FLT_Params ((prc_t *)p);
943}
944
945inline void FLT_Mod( void *p, float v )
946{
947}
948
949// get next filter value for filter pf and input x
950inline int FLT_GetNext( flt_t *pf, int x )
951{
952 return iir_filter( pf->M, pf->a, pf->L, pf->b, pf->w, x );
953}
954
955// batch version for performance
956inline void FLT_GetNextN( flt_t *pflt, portable_samplepair_t *pbuffer, int SampleCount, int op )
957{
958 int count = SampleCount;
959 portable_samplepair_t *pb = pbuffer;
960
961 switch( op )
962 {
963 default:
964 case OP_LEFT:
965 while( count-- )
966 {
967 pb->left = FLT_GetNext( pflt, pb->left );
968 pb++;
969 }
970 break;
971 case OP_RIGHT:
972 while( count-- )
973 {
974 pb->right = FLT_GetNext( pflt, pb->right );
975 pb++;
976 }
977 break;
978 case OP_LEFT_DUPLICATE:
979 while( count-- )
980 {
981 pb->left = pb->right = FLT_GetNext( pflt, pb->left );
982 pb++;
983 }
984 break;
985 }
986}
987
988///////////////////////////////////////////////////////////////////////////
989// Positional updaters for pitch shift etc
990///////////////////////////////////////////////////////////////////////////
991
992// looping position within a wav, with integer and fractional parts
993// used for pitch shifting, upsampling/downsampling
994// 20 bits of fraction, 8+ bits of integer
995typedef struct
996{
997
998 fix20int step; // wave table whole and fractional step value
999 fix20int cstep; // current cummulative step value
1000 int pos; // current position within wav table
1001
1002 int D; // max dimension of array w[0...D] ie: # of samples = D+1
1003} pos_t;
1004
1005// circular wrap of pointer p, relative to array w
1006// D max buffer index w[0...D] (count of samples in buffer is D+1)
1007// i circular index
1008inline void POS_Wrap( int D, int *i )
1009{
1010 if( *i > D ) *i -= D + 1; // when *pi = D + 1, it wraps around to *pi = 0
1011 if( *i < 0 ) *i += D + 1; // when *pi = - 1, it wraps around to *pi = D
1012}
1013
1014// set initial update value - fstep can have no more than 8 bits of integer and 20 bits of fract
1015// D is array max dimension w[0...D] (ie: size D+1)
1016// w is ptr to array
1017// p is ptr to pos_t to initialize
1018inline void POS_Init( pos_t *p, int D, float fstep )
1019{
1020 float step = fstep;
1021
1022 // make sure int part of step is capped at fix20_intmax
1023
1024 if( (int)step > FIX20_INTMAX )
1025 step = (step - (int)step) + FIX20_INTMAX;
1026
1027 p->step = FLOAT_TO_FIX20( step ); // convert fstep to fixed point
1028 p->cstep = 0;
1029 p->pos = 0; // current update value
1030 p->D = D; // always init to end value, in case we're stepping backwards
1031}
1032
1033// change step value - this is an instantaneous change, not smoothed.
1034inline void POS_ChangeVal( pos_t *p, float fstepnew )
1035{
1036 p->step = FLOAT_TO_FIX20( fstepnew ); // convert fstep to fixed point
1037}
1038
1039// return current integer position, then update internal position value
1040inline int POS_GetNext ( pos_t *p )
1041{
1042 // float f = FIX20_TO_FLOAT( p->cstep );
1043 // int i1 = FIX20_INTPART( p->cstep );
1044 // float f1 = FIX20_TO_FLOAT( FIX20_FRACPART( p->cstep ));
1045 // float f2 = FIX20_TO_FLOAT( p->step );
1046
1047 p->cstep += p->step; // update accumulated fraction step value (fixed point)
1048 p->pos += FIX20_INTPART( p->cstep ); // update pos with integer part of accumulated step
1049 p->cstep = FIX20_FRACPART( p->cstep ); // throw away the integer part of accumulated step
1050
1051 // wrap pos around either end of buffer if needed
1052 POS_Wrap( p->D, &( p->pos ));
1053
1054 // make sure returned position is within array bounds
1055 ASSERT( p->pos <= p->D );
1056
1057 return p->pos;
1058}
1059
1060// oneshot position within wav
1061typedef struct
1062{
1063 pos_t p; // pos_t
1064 qboolean fhitend; // flag indicating we hit end of oneshot wav
1065} pos_one_t;
1066
1067// set initial update value - fstep can have no more than 8 bits of integer and 20 bits of fract
1068// one shot position - play only once, don't wrap, when hit end of buffer, return last position
1069inline void POS_ONE_Init( pos_one_t *p1, int D, float fstep )
1070{
1071 POS_Init( &p1->p, D, fstep ) ;
1072
1073 p1->fhitend = false;
1074}
1075
1076// return current integer position, then update internal position value
1077inline int POS_ONE_GetNext( pos_one_t *p1 )
1078{
1079 int pos;
1080 pos_t *p0;
1081
1082 pos = p1->p.pos; // return current position
1083
1084 if( p1->fhitend )
1085 return pos;
1086
1087 p0 = &(p1->p);
1088 p0->cstep += p0->step; // update accumulated fraction step value (fixed point)
1089 p0->pos += FIX20_INTPART( p0->cstep ); // update pos with integer part of accumulated step
1090 //p0->cstep = SIGN(p0->cstep) * FIX20_FRACPART( p0->cstep );
1091 p0->cstep = FIX20_FRACPART( p0->cstep ); // throw away the integer part of accumulated step
1092
1093 // if we wrapped, stop updating, always return last position
1094 // if step value is 0, return hit end
1095
1096 if( !p0->step || p0->pos < 0 || p0->pos >= p0->D )
1097 p1->fhitend = true;
1098 else pos = p0->pos;
1099
1100 // make sure returned value is within array bounds
1101 ASSERT( pos <= p0->D );
1102
1103 return pos;
1104}
1105
1106/////////////////////
1107// Reverbs and delays
1108/////////////////////
1109#define CDLYS 128 // max delay lines active. Also used for lfos.
1110
1111#define DLY_PLAIN 0 // single feedback loop
1112#define DLY_ALLPASS 1 // feedback and feedforward loop - flat frequency response (diffusor)
1113#define DLY_LOWPASS 2 // lowpass filter in feedback loop
1114#define DLY_LINEAR 3 // linear delay, no feedback, unity gain
1115#define DLY_MAX DLY_LINEAR
1116
1117// delay line
1118typedef struct
1119{
1120 qboolean fused; // true if dly is in use
1121 int type; // delay type
1122 int D; // delay size, in samples
1123 int t; // current tap, <= D
1124 int D0; // original delay size (only relevant if calling DLY_ChangeVal)
1125 int *p; // circular buffer pointer
1126 int *w; // array of samples
1127 int a; // feedback value 0..PMAX,normalized to 0-1.0
1128 int b; // gain value 0..PMAX, normalized to 0-1.0
1129 flt_t *pflt; // pointer to filter, if type DLY_LOWPASS
1130 HANDLE h; // memory handle for sample array
1131} dly_t;
1132
1133dly_t dlys[CDLYS]; // delay lines
1134
1135void DLY_Init( dly_t *pdly ) { if( pdly ) Q_memset( pdly, 0, sizeof( dly_t )); }
1136void DLY_InitAll( void ) { int i; for( i = 0; i < CDLYS; i++ ) DLY_Init( &dlys[i] ); }
1137void DLY_Free( dly_t *pdly )
1138{
1139 // free memory buffer
1140 if( pdly )
1141 {
1142 FLT_Free( pdly->pflt );
1143
1144 if( pdly->w )
1145 {
1146 //GlobalUnlock( pdly->h );
1147 free( pdly->h );
1148 }
1149
1150 // free dly slot
1151 Q_memset( pdly, 0, sizeof( dly_t ));
1152 }
1153}
1154
1155
1156void DLY_FreeAll( void ) { int i; for( i = 0; i < CDLYS; i++ ) DLY_Free( &dlys[i] ); }
1157
1158// set up 'b' gain parameter of feedback delay to
1159// compensate for gain caused by feedback.
1160void DLY_SetNormalizingGain( dly_t *pdly )
1161{
1162 // compute normalized gain, set as output gain
1163
1164 // calculate gain of delay line with feedback, and use it to
1165 // reduce output. ie: force delay line with feedback to unity gain
1166
1167 // for constant input x with feedback fb:
1168
1169 // out = x + x*fb + x * fb^2 + x * fb^3...
1170 // gain = out/x
1171 // so gain = 1 + fb + fb^2 + fb^3...
1172 // which, by the miracle of geometric series, equates to 1/1-fb
1173 // thus, gain = 1/(1-fb)
1174
1175 float fgain = 0;
1176 float gain;
1177 int b;
1178
1179 // if b is 0, set b to PMAX (1)
1180 b = pdly->b ? pdly->b : PMAX;
1181
1182 // fgain = b * (1.0 / (1.0 - (float)pdly->a / (float)PMAX)) / (float)PMAX;
1183 fgain = (1.0 / (1.0 - (float)pdly->a / (float)PMAX ));
1184
1185 // compensating gain - multiply rva output by gain then >> PBITS
1186 gain = (int)((1.0 / fgain) * PMAX);
1187
1188 gain = gain * 4; // compensate for fact that gain calculation is for +/- 32767 amplitude wavs
1189 // ie: ok to allow a bit more gain because most wavs are not at theoretical peak amplitude at all times
1190
1191 gain = min( gain, PMAX ); // cap at PMAX
1192 gain = ((float)b/(float)PMAX) * gain; // scale final gain by pdly->b.
1193
1194 pdly->b = (int)gain;
1195}
1196
1197// allocate a new delay line
1198// D number of samples to delay
1199// a feedback value (0-PMAX normalized to 0.0-1.0)
1200// b gain value (0-PMAX normalized to 0.0-1.0)
1201// if DLY_LOWPASS:
1202// L - numerator order of filter
1203// M - denominator order of filter
1204// fb - numerator params, M+1
1205// fa - denominator params, L+1
1206
1207dly_t * DLY_AllocLP( int D, int a, int b, int type, int M, int L, int *fa, int *fb )
1208{
1209 HANDLE h;
1210 int cb;
1211 int *w;
1212 int i;
1213 dly_t *pdly = NULL;
1214
1215 // find open slot
1216 for( i = 0; i < CDLYS; i++ )
1217 {
1218 if( !dlys[i].fused )
1219 {
1220 pdly = &dlys[i];
1221 DLY_Init( pdly );
1222 break;
1223 }
1224 }
1225
1226 if( i == CDLYS )
1227 {
1228 Con_DPrintf("DSP: failed to allocate delay line.\n");
1229 return NULL; // all delay lines in use
1230 }
1231
1232 cb = (D + 1) * sizeof( int ); // assume all samples are signed integers
1233
1234 if( type == DLY_LOWPASS )
1235 {
1236 // alloc lowpass fir_filter
1237 pdly->pflt = FLT_Alloc( M, L, fa, fb );
1238 if( !pdly->pflt )
1239 {
1240 Con_DPrintf("DSP: failed to allocate filter for delay line.\n");
1241 return NULL;
1242 }
1243 }
1244
1245 // alloc delay memory
1246 h = malloc( cb );
1247 if( !h )
1248 {
1249 Sys_Error ("Sound DSP: Out of RAM!");
1250 FLT_Free( pdly->pflt );
1251 return NULL;
1252 }
1253
1254 // lock delay memory
1255 w = (int *) h ;
1256
1257 if( !w )
1258 {
1259 Sys_Error ("Sound DSP: Failed to lock!");
1260 free( h );
1261 FLT_Free( pdly->pflt );
1262 return NULL;
1263 }
1264
1265 // clear delay array
1266 Q_memset( w, 0, cb );
1267
1268 // init values
1269 pdly->type = type;
1270 pdly->D = D;
1271 pdly->t = D; // set delay tap to full delay
1272 pdly->D0 = D;
1273 pdly->p = w; // init circular pointer to head of buffer
1274 pdly->w = w;
1275 pdly->h = h;
1276 pdly->a = min( a, PMAX ); // do not allow 100% feedback
1277 pdly->b = b;
1278 pdly->fused = true;
1279
1280 if( type == DLY_LINEAR )
1281 {
1282 // linear delay has no feedback and unity gain
1283 pdly->a = 0;
1284 pdly->b = PMAX;
1285 }
1286 else
1287 {
1288 // adjust b to compensate for feedback gain
1289 DLY_SetNormalizingGain( pdly );
1290 }
1291
1292 return pdly;
1293}
1294
1295// allocate lowpass or allpass delay
1296dly_t * DLY_Alloc( int D, int a, int b, int type )
1297{
1298 return DLY_AllocLP( D, a, b, type, 0, 0, 0, 0 );
1299}
1300
1301
1302// Allocate new delay, convert from float params in prc preset to internal parameters
1303// Uses filter params in prc if delay is type lowpass
1304
1305// delay parameter order
1306typedef enum
1307{
1308 dly_idtype, // NOTE: first 8 params must match those in mdy_e
1309 dly_idelay,
1310 dly_ifeedback,
1311 dly_igain,
1312 dly_iftype,
1313 dly_icutoff,
1314 dly_iqwidth,
1315 dly_iquality,
1316 dly_cparam
1317} dly_e;
1318
1319
1320// delay parameter ranges
1321prm_rng_t dly_rng[] =
1322{
1323{ dly_cparam, 0, 0 }, // first entry is # of parameters
1324
1325// delay params
1326{ dly_idtype, 0, DLY_MAX }, // delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS
1327{ dly_idelay, 0.0, 1000.0 }, // delay in milliseconds
1328{ dly_ifeedback, 0.0, 0.99 }, // feedback 0-1.0
1329{ dly_igain, 0.0, 1.0 }, // final gain of output stage, 0-1.0
1330
1331// filter params if dly type DLY_LOWPASS
1332{ dly_iftype, 0, FTR_MAX },
1333{ dly_icutoff, 10.0, 22050.0 },
1334{ dly_iqwidth, 100.0, 11025.0 },
1335{ dly_iquality, 0, QUA_MAX },
1336};
1337
1338dly_t * DLY_Params( prc_t *pprc )
1339{
1340 dly_t *pdly = NULL;
1341 int D, a, b;
1342
1343 float delay = pprc->prm[dly_idelay];
1344 float feedback = pprc->prm[dly_ifeedback];
1345 float gain = pprc->prm[dly_igain];
1346 int type = pprc->prm[dly_idtype];
1347
1348 float ftype = pprc->prm[dly_iftype];
1349 float cutoff = pprc->prm[dly_icutoff];
1350 float qwidth = pprc->prm[dly_iqwidth];
1351 float qual = pprc->prm[dly_iquality];
1352
1353 D = MSEC_TO_SAMPS( delay ); // delay samples
1354 a = feedback * PMAX; // feedback
1355 b = gain * PMAX; // gain
1356
1357 switch( type )
1358 {
1359 case DLY_PLAIN:
1360 case DLY_ALLPASS:
1361 case DLY_LINEAR:
1362 pdly = DLY_Alloc( D, a, b, type );
1363 break;
1364 case DLY_LOWPASS:
1365 {
1366 // set up dummy lowpass filter to convert params
1367 prc_t prcf;
1368 flt_t *pflt;
1369
1370 // 0,1,2 - high, medium, low (low quality implies faster execution time)
1371 prcf.prm[flt_iquality] = qual;
1372 prcf.prm[flt_icutoff] = cutoff;
1373 prcf.prm[flt_iftype] = ftype;
1374 prcf.prm[flt_iqwidth] = qwidth;
1375
1376 pflt = (flt_t *)FLT_Params( &prcf );
1377
1378 if( !pflt )
1379 {
1380 Con_DPrintf("DSP: failed to allocate filter.\n");
1381 return NULL;
1382 }
1383
1384 pdly = DLY_AllocLP( D, a, b, type, pflt->M, pflt->L, pflt->a, pflt->b );
1385
1386 FLT_Free( pflt );
1387 break;
1388 }
1389 }
1390 return pdly;
1391}
1392
1393inline void *DLY_VParams( void *p )
1394{
1395 PRC_CheckParams(( prc_t *)p, dly_rng );
1396 return (void *) DLY_Params((prc_t *)p);
1397}
1398
1399// get next value from delay line, move x into delay line
1400int DLY_GetNext( dly_t *pdly, int x )
1401{
1402 switch( pdly->type )
1403 {
1404 default:
1405 case DLY_PLAIN:
1406 return dly_plain( pdly->D, pdly->t, pdly->w, &pdly->p, pdly->a, pdly->b, x );
1407 case DLY_ALLPASS:
1408 return dly_allpass( pdly->D, pdly->t, pdly->w, &pdly->p, pdly->a, pdly->b, x );
1409 case DLY_LOWPASS:
1410 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 );
1411 case DLY_LINEAR:
1412 return dly_linear( pdly->D, pdly->t, pdly->w, &pdly->p, x );
1413 }
1414}
1415
1416// batch version for performance
1417void DLY_GetNextN( dly_t *pdly, portable_samplepair_t *pbuffer, int SampleCount, int op )
1418{
1419 int count = SampleCount;
1420 portable_samplepair_t *pb = pbuffer;
1421
1422 switch( op )
1423 {
1424 default:
1425 case OP_LEFT:
1426 while( count-- )
1427 {
1428 pb->left = DLY_GetNext( pdly, pb->left );
1429 pb++;
1430 }
1431 break;
1432 case OP_RIGHT:
1433 while( count-- )
1434 {
1435 pb->right = DLY_GetNext( pdly, pb->right );
1436 pb++;
1437 }
1438 break;
1439 case OP_LEFT_DUPLICATE:
1440 while( count-- )
1441 {
1442 pb->left = pb->right = DLY_GetNext( pdly, pb->left );
1443 pb++;
1444 }
1445 break;
1446 }
1447}
1448
1449// get tap on t'th sample in delay - don't update buffer pointers, this is done via DLY_GetNext
1450inline int DLY_GetTap( dly_t *pdly, int t )
1451{
1452 return tap( pdly->D, pdly->w, pdly->p, t );
1453}
1454
1455
1456// make instantaneous change to new delay value D.
1457// t tap value must be <= original D (ie: we don't do any reallocation here)
1458void DLY_ChangeVal( dly_t *pdly, int t )
1459{
1460 // never set delay > original delay
1461 pdly->t = min( t, pdly->D0 );
1462}
1463
1464// ignored - use MDY_ for modulatable delay
1465inline void DLY_Mod( void *p, float v )
1466{
1467}
1468
1469///////////////////
1470// Parallel reverbs
1471///////////////////
1472
1473// Reverb A
1474// M parallel reverbs, mixed to mono output
1475
1476#define CRVAS 64 // max number of parallel series reverbs active
1477#define CRVA_DLYS 12 // max number of delays making up reverb_a
1478
1479typedef struct
1480{
1481 qboolean fused;
1482 int m; // number of parallel plain or lowpass delays
1483 int fparallel; // true if filters in parallel with delays, otherwise single output filter
1484 flt_t *pflt;
1485
1486 dly_t *pdlys[CRVA_DLYS]; // array of pointers to delays
1487} rva_t;
1488
1489rva_t rvas[CRVAS];
1490
1491void RVA_Init( rva_t *prva ) { if( prva ) Q_memset( prva, 0, sizeof( rva_t )); }
1492void RVA_InitAll( void ) { int i; for( i = 0; i < CRVAS; i++ ) RVA_Init( &rvas[i] ); }
1493
1494// free parallel series reverb
1495void RVA_Free( rva_t *prva )
1496{
1497 if( prva )
1498 {
1499 int i;
1500
1501 // free all delays
1502 for( i = 0; i < CRVA_DLYS; i++)
1503 DLY_Free ( prva->pdlys[i] );
1504
1505 FLT_Free( prva->pflt );
1506 Q_memset( prva, 0, sizeof (rva_t) );
1507 }
1508}
1509
1510
1511void RVA_FreeAll( void ) { int i; for( i = 0; i < CRVAS; i++ ) RVA_Free( &rvas[i] ); }
1512
1513// create parallel reverb - m parallel reverbs summed
1514// D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
1515// a array of reverb feedback parms for parallel reverbs (CRVB_P_DLYS)
1516// b array of CRVB_P_DLYS - mix params for parallel reverbs
1517// m - number of parallel delays
1518// pflt - filter template, to be used by all parallel delays
1519// fparallel - true if filter operates in parallel with delays, otherwise filter output only
1520rva_t *RVA_Alloc( int *D, int *a, int *b, int m, flt_t *pflt, int fparallel )
1521{
1522 int i;
1523 rva_t *prva;
1524 flt_t *pflt2 = NULL;
1525
1526 // find open slot
1527 for( i = 0; i < CRVAS; i++ )
1528 {
1529 if( !rvas[i].fused )
1530 break;
1531 }
1532
1533 // return null if no free slots
1534 if( i == CRVAS )
1535 {
1536 Con_DPrintf("DSP: failed to allocate reverb.\n");
1537 return NULL;
1538 }
1539
1540 prva = &rvas[i];
1541
1542 // if series filter specified, alloc
1543 if( pflt && !fparallel )
1544 {
1545 // use filter data as template for a filter on output
1546 pflt2 = FLT_Alloc( pflt->M, pflt->L, pflt->a, pflt->b );
1547
1548 if( !pflt2 )
1549 {
1550 Con_DPrintf("DSP: failed to allocate filters for reverb.\n");
1551 return NULL;
1552 }
1553 }
1554
1555 // alloc parallel reverbs
1556 if( pflt && fparallel )
1557 {
1558
1559 // use this filter data as a template to alloc a filter for each parallel delay
1560 for( i = 0; i < m; i++ )
1561 prva->pdlys[i] = DLY_AllocLP( D[i], a[i], b[i], DLY_LOWPASS, pflt->M, pflt->L, pflt->a, pflt->b );
1562 }
1563 else
1564 {
1565 // no filter specified, use plain delays in parallel sections
1566 for( i = 0; i < m; i++ )
1567 prva->pdlys[i] = DLY_Alloc( D[i], a[i], b[i], DLY_PLAIN );
1568 }
1569
1570
1571 // if we failed to alloc any reverb, free all, return NULL
1572 for( i = 0; i < m; i++ )
1573 {
1574 if( !prva->pdlys[i] )
1575 {
1576 FLT_Free( pflt2 );
1577 RVA_Free( prva );
1578 Con_DPrintf("DSP: failed to allocate delays for reverb.\n");
1579 return NULL;
1580 }
1581 }
1582
1583 prva->fused = true;
1584 prva->m = m;
1585 prva->fparallel = fparallel;
1586 prva->pflt = pflt2;
1587
1588 return prva;
1589}
1590
1591
1592// parallel reverberator
1593//
1594// for each input sample x do:
1595// x0 = plain(D0,w0,&p0,a0,x)
1596// x1 = plain(D1,w1,&p1,a1,x)
1597// x2 = plain(D2,w2,&p2,a2,x)
1598// x3 = plain(D3,w3,&p3,a3,x)
1599// y = b0*x0 + b1*x1 + b2*x2 + b3*x3
1600//
1601// rgdly - array of 6 delays:
1602// D - Delay values (typical - 29, 37, 44, 50, 27, 31)
1603// w - array of delayed values
1604// p - array of pointers to circular delay line pointers
1605// a - array of 6 feedback values (typical - all equal, like 0.75 * PMAX)
1606// b - array of 6 gain values for plain reverb outputs (1, .9, .8, .7)
1607// xin - input value
1608// if fparallel, filters are built into delays,
1609// otherwise, filter output
1610
1611inline int RVA_GetNext( rva_t *prva, int x )
1612{
1613 int m = prva->m;
1614 int i, y, sum;
1615
1616 sum = 0;
1617
1618 for( i = 0; i < m; i++ )
1619 sum += DLY_GetNext( prva->pdlys[i], x );
1620
1621 // m is clamped between RVA_BASEM & CRVA_DLYS
1622
1623 if( m ) y = sum/m;
1624 else y = x;
1625#if 0
1626 // PERFORMANCE:
1627 // UNDONE: build as array
1628 int mm;
1629
1630 switch( m )
1631 {
1632 case 12: mm = (PMAX/12); break;
1633 case 11: mm = (PMAX/11); break;
1634 case 10: mm = (PMAX/10); break;
1635 case 9: mm = (PMAX/9); break;
1636 case 8: mm = (PMAX/8); break;
1637 case 7: mm = (PMAX/7); break;
1638 case 6: mm = (PMAX/6); break;
1639 case 5: mm = (PMAX/5); break;
1640 case 4: mm = (PMAX/4); break;
1641 case 3: mm = (PMAX/3); break;
1642 case 2: mm = (PMAX/2); break;
1643 default:
1644 case 1: mm = (PMAX/1); break;
1645 }
1646
1647 y = (sum * mm) >> PBITS;
1648
1649#endif // 0
1650
1651 // run series filter if present
1652 if( prva->pflt && !prva->fparallel )
1653 y = FLT_GetNext( prva->pflt, y );
1654
1655 return y;
1656}
1657
1658// batch version for performance
1659inline void RVA_GetNextN( rva_t *prva, portable_samplepair_t *pbuffer, int SampleCount, int op )
1660{
1661 int count = SampleCount;
1662 portable_samplepair_t *pb = pbuffer;
1663
1664 switch( op )
1665 {
1666 default:
1667 case OP_LEFT:
1668 while( count-- )
1669 {
1670 pb->left = RVA_GetNext( prva, pb->left );
1671 pb++;
1672 }
1673 break;
1674 case OP_RIGHT:
1675 while( count-- )
1676 {
1677 pb->right = RVA_GetNext( prva, pb->right );
1678 pb++;
1679 }
1680 break;
1681 case OP_LEFT_DUPLICATE:
1682 while( count-- )
1683 {
1684 pb->left = pb->right = RVA_GetNext( prva, pb->left );
1685 pb++;
1686 }
1687 break;
1688 }
1689}
1690
1691#define RVA_BASEM 3 // base number of parallel delays
1692
1693// nominal delay and feedback values
1694
1695//float rvadlys[] = { 29, 37, 44, 50, 62, 75, 96, 118, 127, 143, 164, 175 };
1696float rvadlys[] = { 18, 23, 28, 36, 47, 21, 26, 33, 40, 49, 45, 38 };
1697float 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 };
1698
1699// reverb parameter order
1700typedef enum
1701{
1702 // parameter order
1703 rva_isize,
1704 rva_idensity,
1705 rva_idecay,
1706 rva_iftype,
1707 rva_icutoff,
1708 rva_iqwidth,
1709 rva_ifparallel,
1710 rva_cparam // # of params
1711} rva_e;
1712
1713// filter parameter ranges
1714prm_rng_t rva_rng[] =
1715{
1716{ rva_cparam, 0, 0 }, // first entry is # of parameters
1717
1718// reverb params
1719{ rva_isize, 0.0, 2.0 }, // 0-2.0 scales nominal delay parameters (starting at approx 20ms)
1720{ rva_idensity, 0.0, 2.0 }, // 0-2.0 density of reverbs (room shape) - controls # of parallel or series delays
1721{ rva_idecay, 0.0, 2.0 }, // 0-2.0 scales feedback parameters (starting at approx 0.15)
1722
1723// filter params for each parallel reverb (quality set to 0 for max execution speed)
1724{ rva_iftype, 0, FTR_MAX },
1725{ rva_icutoff, 10, 22050 },
1726{ rva_iqwidth, 100, 11025 },
1727{ rva_ifparallel, 0, 1 } // if 1, then all filters operate in parallel with delays. otherwise filter output only
1728};
1729
1730rva_t * RVA_Params( prc_t *pprc )
1731{
1732 flt_t *pflt;
1733 rva_t *prva;
1734 float size = pprc->prm[rva_isize]; // 0-2.0 controls scale of delay parameters
1735 float density = pprc->prm[rva_idensity]; // 0-2.0 density of reverbs (room shape) - controls # of parallel delays
1736 float decay = pprc->prm[rva_idecay]; // 0-1.0 controls feedback parameters
1737
1738 float ftype = pprc->prm[rva_iftype];
1739 float cutoff = pprc->prm[rva_icutoff];
1740 float qwidth = pprc->prm[rva_iqwidth];
1741
1742 float fparallel = pprc->prm[rva_ifparallel];
1743
1744 // D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
1745 // a array of reverb feedback parms for parallel delays
1746 // b array of CRVB_P_DLYS - mix params for parallel reverbs
1747 // m - number of parallel delays
1748
1749 int D[CRVA_DLYS];
1750 int a[CRVA_DLYS];
1751 int b[CRVA_DLYS];
1752 int m = RVA_BASEM;
1753 int i;
1754
1755 m = density * CRVA_DLYS / 2;
1756
1757 // limit # delays 3-12
1758 m = bound( RVA_BASEM, m, CRVA_DLYS );
1759
1760 // average time sound takes to travel from most distant wall
1761 // (cap at 1000 ft room)
1762 for( i = 0; i < m; i++ )
1763 {
1764 // delays of parallel reverb
1765 D[i] = MSEC_TO_SAMPS( rvadlys[i] * size );
1766
1767 // feedback and gain of parallel reverb
1768 a[i] = (int)min( 0.9 * PMAX, rvafbs[i] * (float)PMAX * decay );
1769 b[i] = PMAX;
1770 }
1771
1772 // add filter
1773 pflt = NULL;
1774
1775 if( cutoff )
1776 {
1777 // set up dummy lowpass filter to convert params
1778 prc_t prcf;
1779
1780 prcf.prm[flt_iquality] = QUA_LO; // force filter to low quality for faster execution time
1781 prcf.prm[flt_icutoff] = cutoff;
1782 prcf.prm[flt_iftype] = ftype;
1783 prcf.prm[flt_iqwidth] = qwidth;
1784
1785 pflt = (flt_t *)FLT_Params( &prcf );
1786 }
1787
1788 prva = RVA_Alloc( D, a, b, m, pflt, fparallel );
1789 FLT_Free( pflt );
1790
1791 return prva;
1792}
1793
1794inline void *RVA_VParams( void *p )
1795{
1796 PRC_CheckParams((prc_t *)p, rva_rng );
1797 return (void *)RVA_Params((prc_t *)p );
1798}
1799
1800inline void RVA_Mod( void *p, float v )
1801{
1802}
1803
1804
1805////////////
1806// Diffusor
1807///////////
1808
1809// (N series allpass reverbs)
1810#define CDFRS 64 // max number of series reverbs active
1811#define CDFR_DLYS 16 // max number of delays making up diffusor
1812
1813typedef struct
1814{
1815 qboolean fused;
1816 int n; // series allpass delays
1817 int w[CDFR_DLYS]; // internal state array for series allpass filters
1818 dly_t *pdlys[CDFR_DLYS]; // array of pointers to delays
1819} dfr_t;
1820
1821dfr_t dfrs[CDFRS];
1822
1823void DFR_Init( dfr_t *pdfr ) { if( pdfr ) Q_memset( pdfr, 0, sizeof( dfr_t )); }
1824void DFR_InitAll( void ) { int i; for( i = 0; i < CDFRS; i++ ) DFR_Init ( &dfrs[i] ); }
1825
1826// free parallel series reverb
1827void DFR_Free( dfr_t *pdfr )
1828{
1829 if( pdfr )
1830 {
1831 int i;
1832
1833 // free all delays
1834 for( i = 0; i < CDFR_DLYS; i++ )
1835 DLY_Free( pdfr->pdlys[i] );
1836
1837 Q_memset( pdfr, 0, sizeof( dfr_t ));
1838 }
1839}
1840
1841
1842void DFR_FreeAll( void ) { int i; for( i = 0; i < CDFRS; i++ ) DFR_Free( &dfrs[i] ); }
1843
1844// create n series allpass reverbs
1845// D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
1846// a array of reverb feedback parms for series delays
1847// b array of gain params for parallel reverbs
1848// n - number of series delays
1849
1850dfr_t *DFR_Alloc( int *D, int *a, int *b, int n )
1851{
1852 int i;
1853 dfr_t *pdfr;
1854
1855 // find open slot
1856 for( i = 0; i < CDFRS; i++ )
1857 {
1858 if( !dfrs[i].fused )
1859 break;
1860 }
1861
1862 // return null if no free slots
1863 if( i == CDFRS )
1864 {
1865 Con_DPrintf("DSP: failed to allocate diffusor.\n");
1866 return NULL;
1867 }
1868
1869 pdfr = &dfrs[i];
1870
1871 DFR_Init( pdfr );
1872
1873 // alloc reverbs
1874 for( i = 0; i < n; i++ )
1875 pdfr->pdlys[i] = DLY_Alloc( D[i], a[i], b[i], DLY_ALLPASS );
1876
1877 // if we failed to alloc any reverb, free all, return NULL
1878 for( i = 0; i < n; i++ )
1879 {
1880 if( !pdfr->pdlys[i] )
1881 {
1882 DFR_Free( pdfr );
1883 Con_DPrintf("DSP: failed to allocate delays for diffusor.\n");
1884 return NULL;
1885 }
1886 }
1887
1888 pdfr->fused = true;
1889 pdfr->n = n;
1890
1891 return pdfr;
1892}
1893
1894// series reverberator
1895inline int DFR_GetNext( dfr_t *pdfr, int x )
1896{
1897 int i, y;
1898 int n = pdfr->n;
1899
1900 y = x;
1901 for( i = 0; i < n; i++ )
1902 y = DLY_GetNext( pdfr->pdlys[i], y );
1903 return y;
1904
1905#if 0
1906 // alternate method, using internal state - causes PREDELAY = sum of delay times
1907
1908 int *v = pdfr->w; // intermediate results
1909
1910 v[0] = x;
1911
1912 // reverse evaluate series delays
1913 // w[0] w[1] w[2] w[n-1] w[n]
1914 // x---->D[0]--->D[1]--->D[2]...-->D[n-1]--->out
1915 //
1916
1917 for( i = n; i > 0; i-- )
1918 v[i] = DLY_GetNext( pdfr->pdlys[i-1], v[i-1] );
1919
1920 return v[n];
1921#endif
1922}
1923
1924// batch version for performance
1925inline void DFR_GetNextN( dfr_t *pdfr, portable_samplepair_t *pbuffer, int SampleCount, int op )
1926{
1927 int count = SampleCount;
1928 portable_samplepair_t *pb = pbuffer;
1929
1930 switch( op )
1931 {
1932 default:
1933 case OP_LEFT:
1934 while( count-- )
1935 {
1936 pb->left = DFR_GetNext( pdfr, pb->left );
1937 pb++;
1938 }
1939 break;
1940 case OP_RIGHT:
1941 while( count-- )
1942 {
1943 pb->right = DFR_GetNext( pdfr, pb->right );
1944 pb++;
1945 }
1946 break;
1947 case OP_LEFT_DUPLICATE:
1948 while( count-- )
1949 {
1950 pb->left = pb->right = DFR_GetNext( pdfr, pb->left );
1951 pb++;
1952 }
1953 break;
1954 }
1955}
1956
1957#define DFR_BASEN 2 // base number of series allpass delays
1958
1959// nominal diffusor delay and feedback values
1960//float dfrdlys[] = { 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95 };
1961float dfrdlys[] = { 13, 19, 26, 21, 32, 36, 38, 16, 24, 28, 41, 35, 10, 46, 50, 27 };
1962float dfrfbs[] = { 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15 };
1963
1964
1965// diffusor parameter order
1966
1967typedef enum
1968{
1969 // parameter order
1970 dfr_isize,
1971 dfr_idensity,
1972 dfr_idecay,
1973 dfr_cparam // # of params
1974
1975} dfr_e;
1976
1977// diffusor parameter ranges
1978
1979prm_rng_t dfr_rng[] =
1980{
1981{ dfr_cparam, 0, 0 }, // first entry is # of parameters
1982{ dfr_isize, 0.0, 1.0 }, // 0-1.0 scales all delays
1983{ dfr_idensity, 0.0, 1.0 }, // 0-1.0 controls # of series delays
1984{ dfr_idecay, 0.0, 1.0 }, // 0-1.0 scales all feedback parameters
1985};
1986
1987dfr_t *DFR_Params( prc_t *pprc )
1988{
1989 dfr_t *pdfr;
1990 int i, s;
1991 float size = pprc->prm[dfr_isize]; // 0-1.0 scales all delays
1992 float density = pprc->prm[dfr_idensity]; // 0-1.0 controls # of series delays
1993 float diffusion = pprc->prm[dfr_idecay]; // 0-1.0 scales all feedback parameters
1994
1995 // D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
1996 // a array of reverb feedback parms for series delays (CRVB_S_DLYS)
1997 // b gain of each reverb section
1998 // n - number of series delays
1999
2000 int D[CDFR_DLYS];
2001 int a[CDFR_DLYS];
2002 int b[CDFR_DLYS];
2003 int n = DFR_BASEN;
2004
2005 // increase # of series diffusors with increased density
2006 n += density * 2;
2007
2008 // limit m, n to half max number of delays
2009 n = min( CDFR_DLYS / 2, n );
2010
2011 // compute delays for diffusors
2012 for( i = 0; i < n; i++ )
2013 {
2014 s = (int)( dfrdlys[i] * size );
2015
2016 // delay of diffusor
2017 D[i] = MSEC_TO_SAMPS( s );
2018
2019 // feedback and gain of diffusor
2020 a[i] = min( 0.9 * PMAX, dfrfbs[i] * PMAX * diffusion );
2021 b[i] = PMAX;
2022 }
2023
2024 pdfr = DFR_Alloc( D, a, b, n );
2025
2026 return pdfr;
2027}
2028
2029inline void *DFR_VParams( void *p )
2030{
2031 PRC_CheckParams((prc_t *)p, dfr_rng );
2032 return (void *)DFR_Params((prc_t *)p );
2033}
2034
2035inline void DFR_Mod( void *p, float v )
2036{
2037}
2038
2039//////////////////////
2040// LFO wav definitions
2041//////////////////////
2042
2043#define CLFOSAMPS 512 // samples per wav table - single cycle only
2044#define LFOBITS 14 // bits of peak amplitude of lfo wav
2045#define LFOAMP ((1<<LFOBITS)-1) // peak amplitude of lfo wav
2046
2047//types of lfo wavs
2048
2049#define LFO_SIN 0 // sine wav
2050#define LFO_TRI 1 // triangle wav
2051#define LFO_SQR 2 // square wave, 50% duty cycle
2052#define LFO_SAW 3 // forward saw wav
2053#define LFO_RND 4 // random wav
2054#define LFO_LOG_IN 5 // logarithmic fade in
2055#define LFO_LOG_OUT 6 // logarithmic fade out
2056#define LFO_LIN_IN 7 // linear fade in
2057#define LFO_LIN_OUT 8 // linear fade out
2058#define LFO_MAX LFO_LIN_OUT
2059
2060#define CLFOWAV 9 // number of LFO wav tables
2061
2062typedef struct // lfo or envelope wave table
2063{
2064 int type; // lfo type
2065 dly_t *pdly; // delay holds wav values and step pointers
2066} lfowav_t;
2067
2068lfowav_t lfowavs[CLFOWAV];
2069
2070// deallocate lfo wave table. Called only when sound engine exits.
2071void LFOWAV_Free( lfowav_t *plw )
2072{
2073 // free delay
2074 if( plw ) DLY_Free( plw->pdly );
2075
2076 Q_memset( plw, 0, sizeof( lfowav_t ));
2077}
2078
2079// deallocate all lfo wave tables. Called only when sound engine exits.
2080void LFOWAV_FreeAll( void )
2081{
2082 int i;
2083
2084 for( i = 0; i < CLFOWAV; i++ )
2085 LFOWAV_Free( &lfowavs[i] );
2086}
2087
2088// fill lfo array w with count samples of lfo type 'type'
2089// all lfo wavs except fade out, rnd, and log_out should start with 0 output
2090void LFOWAV_Fill( int *w, int count, int type )
2091{
2092 int i,x;
2093
2094 switch( type )
2095 {
2096 default:
2097 case LFO_SIN: // sine wav, all values 0 <= x <= LFOAMP, initial value = 0
2098 for( i = 0; i < count; i++ )
2099 {
2100 x = ( int )(( float)(LFOAMP) * sin( (M_PI2 * (float)i / (float)count ) + ( M_PI_F * 1.5 )));
2101 w[i] = (x + LFOAMP)/2;
2102 }
2103 break;
2104 case LFO_TRI: // triangle wav, all values 0 <= x <= LFOAMP, initial value = 0
2105 for( i = 0; i < count; i++ )
2106 {
2107 w[i] = ( int ) ( (float)(2 * LFOAMP * i ) / (float)(count) );
2108
2109 if( i > count / 2 )
2110 w[i] = ( int )( (float) (2 * LFOAMP) - (float)( 2 * LFOAMP * i ) / (float)( count ));
2111 }
2112 break;
2113 case LFO_SQR: // square wave, 50% duty cycle, all values 0 <= x <= LFOAMP, initial value = 0
2114 for( i = 0; i < count; i++ )
2115 w[i] = i > count / 2 ? 0 : LFOAMP;
2116 break;
2117 case LFO_SAW: // forward saw wav, aall values 0 <= x <= LFOAMP, initial value = 0
2118 for( i = 0; i < count; i++ )
2119 w[i] = ( int )( (float)(LFOAMP) * (float)i / (float)( count ));
2120 break;
2121 case LFO_RND: // random wav, all values 0 <= x <= LFOAMP
2122 for( i = 0; i < count; i++ )
2123 w[i] = ( int )( Com_RandomLong( 0, LFOAMP ));
2124 break;
2125 case LFO_LOG_IN: // logarithmic fade in, all values 0 <= x <= LFOAMP, initial value = 0
2126 for( i = 0; i < count; i++ )
2127 w[i] = ( int ) ( (float)(LFOAMP) * pow( (float)i / (float)count, 2 ));
2128 break;
2129 case LFO_LOG_OUT: // logarithmic fade out, all values 0 <= x <= LFOAMP, initial value = LFOAMP
2130 for( i = 0; i < count; i++ )
2131 w[i] = ( int ) ( (float)(LFOAMP) * pow( 1.0 - ((float)i / (float)count), 2 ));
2132 break;
2133 case LFO_LIN_IN: // linear fade in, all values 0 <= x <= LFOAMP, initial value = 0
2134 for( i = 0; i < count; i++ )
2135 w[i] = ( int )( (float)(LFOAMP) * (float)i / (float)(count) );
2136 break;
2137 case LFO_LIN_OUT: // linear fade out, all values 0 <= x <= LFOAMP, initial value = LFOAMP
2138 for( i = 0; i < count; i++ )
2139 w[i] = LFOAMP - ( int )( (float)(LFOAMP) * (float)i / (float)(count) );
2140 break;
2141 }
2142}
2143
2144// allocate all lfo wave tables. Called only when sound engine loads.
2145void LFOWAV_InitAll( void )
2146{
2147 int i;
2148 dly_t *pdly;
2149
2150 Q_memset( lfowavs, 0, sizeof( lfowavs ));
2151
2152 // alloc space for each lfo wav type
2153 for( i = 0; i < CLFOWAV; i++ )
2154 {
2155 pdly = DLY_Alloc( CLFOSAMPS, 0, 0 , DLY_PLAIN );
2156
2157 lfowavs[i].pdly = pdly;
2158 lfowavs[i].type = i;
2159
2160 LFOWAV_Fill( pdly->w, CLFOSAMPS, i );
2161 }
2162
2163 // if any dlys fail to alloc, free all
2164 for( i = 0; i < CLFOWAV; i++ )
2165 {
2166 if( !lfowavs[i].pdly )
2167 LFOWAV_FreeAll();
2168 }
2169}
2170
2171
2172////////////////////////////////////////
2173// LFO iterators - one shot and looping
2174////////////////////////////////////////
2175
2176#define CLFO 16 // max active lfos (this steals from active delays)
2177
2178typedef struct
2179{
2180 qboolean fused; // true if slot take
2181 dly_t *pdly; // delay points to lfo wav within lfowav_t (don't free this)
2182 float f; // playback frequency in hz
2183 pos_t pos; // current position within wav table, looping
2184 pos_one_t pos1; // current position within wav table, one shot
2185 int foneshot; // true - one shot only, don't repeat
2186} lfo_t;
2187
2188lfo_t lfos[CLFO];
2189
2190void LFO_Init( lfo_t *plfo ) { if( plfo ) Q_memset( plfo, 0, sizeof( lfo_t )); }
2191void LFO_InitAll( void ) { int i; for( i = 0; i < CLFO; i++ ) LFO_Init( &lfos[i] ); }
2192void LFO_Free( lfo_t *plfo ) { if( plfo ) Q_memset( plfo, 0, sizeof( lfo_t )); }
2193void LFO_FreeAll( void ) { int i; for( i = 0; i < CLFO; i++ ) LFO_Free( &lfos[i] ); }
2194
2195
2196// get step value given desired playback frequency
2197inline float LFO_HzToStep( float freqHz )
2198{
2199 float lfoHz;
2200
2201 // calculate integer and fractional step values,
2202 // assume an update rate of SOUND_DMA_SPEED samples/sec
2203
2204 // 1 cycle/CLFOSAMPS * SOUND_DMA_SPEED samps/sec = cycles/sec = current lfo rate
2205 //
2206 // lforate * X = freqHz so X = freqHz/lforate = update rate
2207 lfoHz = (float)(SOUND_DMA_SPEED) / (float)(CLFOSAMPS);
2208
2209 return freqHz / lfoHz;
2210}
2211
2212// return pointer to new lfo
2213
2214lfo_t *LFO_Alloc( int wtype, float freqHz, qboolean foneshot )
2215{
2216 int i, type = min( CLFOWAV - 1, wtype );
2217 float lfostep;
2218
2219 for( i = 0; i < CLFO; i++ )
2220 {
2221 if( !lfos[i].fused )
2222 {
2223 lfo_t *plfo = &lfos[i];
2224
2225 LFO_Init( plfo );
2226
2227 plfo->fused = true;
2228 plfo->pdly = lfowavs[type].pdly; // pdly in lfo points to wav table data in lfowavs
2229 plfo->f = freqHz;
2230 plfo->foneshot = foneshot;
2231
2232 lfostep = LFO_HzToStep( freqHz );
2233
2234 // init positional pointer (ie: fixed point updater for controlling pitch of lfo)
2235 if( !foneshot ) POS_Init(&(plfo->pos), plfo->pdly->D, lfostep );
2236 else POS_ONE_Init(&(plfo->pos1), plfo->pdly->D,lfostep );
2237
2238 return plfo;
2239 }
2240 }
2241
2242 Con_DPrintf("DSP: failed to allocate LFO.\n");
2243 return NULL;
2244}
2245
2246// get next lfo value
2247// Value returned is 0..LFOAMP. can be normalized by shifting right by LFOBITS
2248// To play back at correct passed in frequency, routien should be
2249// called once for every output sample (ie: at SOUND_DMA_SPEED)
2250// x is dummy param
2251inline int LFO_GetNext( lfo_t *plfo, int x )
2252{
2253 int i;
2254
2255 // get current position
2256 if( !plfo->foneshot ) i = POS_GetNext( &plfo->pos );
2257 else i = POS_ONE_GetNext( &plfo->pos1 );
2258
2259 // return current sample
2260 return plfo->pdly->w[i];
2261}
2262
2263// batch version for performance
2264inline void LFO_GetNextN( lfo_t *plfo, portable_samplepair_t *pbuffer, int SampleCount, int op )
2265{
2266 int count = SampleCount;
2267 portable_samplepair_t *pb = pbuffer;
2268
2269 switch( op )
2270 {
2271 default:
2272 case OP_LEFT:
2273 while( count-- )
2274 {
2275 pb->left = LFO_GetNext( plfo, pb->left );
2276 pb++;
2277 }
2278 break;
2279 case OP_RIGHT:
2280 while( count-- )
2281 {
2282 pb->right = LFO_GetNext( plfo, pb->right );
2283 pb++;
2284 }
2285 break;
2286 case OP_LEFT_DUPLICATE:
2287 while( count-- )
2288 {
2289 pb->left = pb->right = LFO_GetNext( plfo, pb->left );
2290 pb++;
2291 }
2292 break;
2293 }
2294}
2295
2296// uses lfowav, rate, foneshot
2297typedef enum
2298{
2299 // parameter order
2300 lfo_iwav,
2301 lfo_irate,
2302 lfo_ifoneshot,
2303 lfo_cparam // # of params
2304
2305} lfo_e;
2306
2307// parameter ranges
2308
2309prm_rng_t lfo_rng[] =
2310{
2311{ lfo_cparam, 0, 0 }, // first entry is # of parameters
2312{ lfo_iwav, 0.0, LFO_MAX }, // lfo type to use (LFO_SIN, LFO_RND...)
2313{ lfo_irate, 0.0, 16000.0 }, // modulation rate in hz. for MDY, 1/rate = 'glide' time in seconds
2314{ lfo_ifoneshot, 0.0, 1.0 }, // 1.0 if lfo is oneshot
2315};
2316
2317lfo_t * LFO_Params( prc_t *pprc )
2318{
2319 lfo_t *plfo;
2320 qboolean foneshot = pprc->prm[lfo_ifoneshot] > 0 ? true : false;
2321
2322 plfo = LFO_Alloc( pprc->prm[lfo_iwav], pprc->prm[lfo_irate], foneshot );
2323
2324 return plfo;
2325}
2326
2327void LFO_ChangeVal( lfo_t *plfo, float fhz )
2328{
2329 float fstep = LFO_HzToStep( fhz );
2330
2331 // change lfo playback rate to new frequency fhz
2332 if( plfo->foneshot ) POS_ChangeVal( &plfo->pos, fstep );
2333 else POS_ChangeVal( &plfo->pos1.p, fstep );
2334}
2335
2336inline void *LFO_VParams( void *p )
2337{
2338 PRC_CheckParams((prc_t *)p, lfo_rng );
2339 return (void *)LFO_Params((prc_t *)p);
2340}
2341
2342// v is +/- 0-1.0
2343// v changes current lfo frequency up/down by +/- v%
2344inline void LFO_Mod( lfo_t *plfo, float v )
2345{
2346 float fhz;
2347 float fhznew;
2348
2349 fhz = plfo->f;
2350 fhznew = fhz * (1.0 + v);
2351
2352 LFO_ChangeVal( plfo, fhznew );
2353
2354 return;
2355}
2356
2357
2358/////////////////////////////////////////////////////////////////////////////
2359// Ramp - used for varying smoothly between int parameters ie: modulation delays
2360/////////////////////////////////////////////////////////////////////////////
2361typedef struct
2362{
2363 int initval; // initial ramp value
2364 int target; // final ramp value
2365 int sign; // increasing (1) or decreasing (-1) ramp
2366 int yprev; // previous output value
2367 qboolean fhitend; // true if hit end of ramp
2368 pos_one_t ps; // current ramp output
2369} rmp_t;
2370
2371// ramp smoothly between initial value and target value in approx 'ramptime' seconds.
2372// (initial value may be greater or less than target value)
2373// never changes output by more than +1 or -1 (which can cause the ramp to take longer to complete than ramptime)
2374// called once per sample while ramping
2375// ramptime - duration of ramp in seconds
2376// initval - initial ramp value
2377// targetval - target ramp value
2378void RMP_Init( rmp_t *prmp, float ramptime, int initval, int targetval )
2379{
2380 int rise;
2381 int run;
2382
2383 if( prmp ) Q_memset( prmp, 0, sizeof( rmp_t ));
2384
2385 run = (int)( ramptime * SOUND_DMA_SPEED ); // 'samples' in ramp
2386 rise = (targetval - initval); // height of ramp
2387
2388 // init fixed point iterator to iterate along the height of the ramp 'rise'
2389 // always iterates from 0..'rise', increasing in value
2390
2391 POS_ONE_Init( &prmp->ps, ABS( rise ), ABS((float) rise) / ((float) run));
2392
2393 prmp->yprev = initval;
2394 prmp->initval = initval;
2395 prmp->target = targetval;
2396 prmp->sign = SIGN( rise );
2397
2398}
2399
2400// continues from current position to new target position
2401void RMP_SetNext( rmp_t *prmp, float ramptime, int targetval )
2402{
2403 RMP_Init( prmp, ramptime, prmp->yprev, targetval );
2404}
2405
2406inline qboolean RMP_HitEnd( rmp_t *prmp )
2407{
2408 return prmp->fhitend;
2409}
2410
2411inline void RMP_SetEnd( rmp_t *prmp )
2412{
2413 prmp->fhitend = true;
2414}
2415
2416// get next ramp value & update ramp, never varies by more than +1 or -1 between calls
2417// when ramp hits target value, it thereafter always returns last value
2418
2419inline int RMP_GetNext( rmp_t *prmp )
2420{
2421 int y, d;
2422
2423 // if we hit ramp end, return last value
2424 if( prmp->fhitend )
2425 return prmp->yprev;
2426
2427 // get next integer position in ramp height.
2428 d = POS_ONE_GetNext( &prmp->ps );
2429
2430 if( prmp->ps.fhitend )
2431 prmp->fhitend = true;
2432
2433 // increase or decrease from initval, depending on ramp sign
2434 if( prmp->sign > 0 )
2435 y = prmp->initval + d;
2436 else y = prmp->initval - d;
2437
2438 // only update current height by a max of +1 or -1
2439 // this means that for short ramp times, we may not hit target
2440 if( ABS( y - prmp->yprev ) >= 1 )
2441 prmp->yprev += prmp->sign;
2442
2443 return prmp->yprev;
2444}
2445
2446// get current ramp value, don't update ramp
2447inline int RMP_GetCurrent( rmp_t *prmp )
2448{
2449 return prmp->yprev;
2450}
2451
2452////////////////////////////////////////
2453// Time Compress/expand with pitch shift
2454////////////////////////////////////////
2455
2456// realtime pitch shift - ie: pitch shift without change to playback rate
2457
2458#define CPTCS 64
2459
2460typedef struct
2461{
2462 qboolean fused;
2463 dly_t *pdly_in; // input buffer space
2464 dly_t *pdly_out; // output buffer space
2465 int *pin; // input buffer (pdly_in->w)
2466 int *pout; // output buffer (pdly_out->w)
2467 int cin; // # samples in input buffer
2468 int cout; // # samples in output buffer
2469 int cxfade; // # samples in crossfade segment
2470 int ccut; // # samples to cut
2471 int cduplicate; // # samples to duplicate (redundant - same as ccut)
2472 int iin; // current index into input buffer (reading)
2473 pos_one_t psn; // stepping index through output buffer
2474 qboolean fdup; // true if duplicating, false if cutting
2475 float fstep; // pitch shift & time compress/expand
2476} ptc_t;
2477
2478ptc_t ptcs[CPTCS];
2479
2480void PTC_Init( ptc_t *pptc ) { if( pptc ) Q_memset( pptc, 0, sizeof( ptc_t )); };
2481void PTC_Free( ptc_t *pptc )
2482{
2483 if( pptc )
2484 {
2485 DLY_Free( pptc->pdly_in );
2486 DLY_Free( pptc->pdly_out );
2487
2488 Q_memset( pptc, 0, sizeof( ptc_t ));
2489 }
2490};
2491
2492void PTC_InitAll() { int i; for( i = 0; i < CPTCS; i++ ) PTC_Init( &ptcs[i] ); };
2493void PTC_FreeAll() { int i; for( i = 0; i < CPTCS; i++ ) PTC_Free( &ptcs[i] ); };
2494
2495// Time compressor/expander with pitch shift (ie: pitch changes, playback rate does not)
2496//
2497// Algorithm:
2498// 1) Duplicate or discard chunks of sound to provide tslice * fstep seconds of sound.
2499// (The user-selectable size of the buffer to process is tslice milliseconds in length)
2500// 2) Resample this compressed/expanded buffer at fstep to produce a pitch shifted
2501// output with the same duration as the input (ie: #samples out = # samples in, an
2502// obvious requirement for realtime _inline processing).
2503
2504// timeslice is size in milliseconds of full buffer to process.
2505// timeslice * fstep is the size of the expanded/compressed buffer
2506// timexfade is length in milliseconds of crossfade region between duplicated or cut sections
2507// fstep is % expanded/compressed sound normalized to 0.01-2.0 (1% - 200%)
2508
2509// input buffer:
2510
2511// iin-->
2512
2513// [0... tslice ...D] input samples 0...D (D is NEWEST sample)
2514// [0... ...n][m... tseg ...D] region to be cut or duplicated m...D
2515
2516// [0... [p..txf1..n][m... tseg ...D] fade in region 1 txf1 p...n
2517// [0... ...n][m..[q..txf2..D] fade out region 2 txf2 q...D
2518
2519
2520// pitch up: duplicate into output buffer: tdup = tseg
2521
2522// [0... ...n][m... tdup ...D][m... tdup ...D] output buffer size with duplicate region
2523// [0... ...n][m..[p...xf1..n][m... tdup ...D] fade in p...n while fading out q...D
2524// [0... ...n][m..[q...xf2..D][m... tdup ...D]
2525// [0... ...n][m..[.XFADE...n][m... tdup ...D] final duplicated output buffer - resample at fstep
2526
2527// pitch down: cut into output buffer: tcut = tseg
2528
2529// [0... ...n][m... tcut ...D] input samples with cut region delineated m...D
2530// [0... ...n] output buffer size after cut
2531// [0... [q..txf2...D] fade in txf1 q...D while fade out txf2 p...n
2532// [0... [.XFADE ...D] final cut output buffer - resample at fstep
2533
2534
2535ptc_t * PTC_Alloc( float timeslice, float timexfade, float fstep )
2536{
2537 int i;
2538 ptc_t *pptc;
2539 float tout;
2540 int cin, cout;
2541 float tslice = timeslice;
2542 float txfade = timexfade;
2543 float tcutdup;
2544
2545 // find time compressor slot
2546 for( i = 0; i < CPTCS; i++ )
2547 {
2548 if( !ptcs[i].fused )
2549 break;
2550 }
2551
2552 if( i == CPTCS )
2553 {
2554 Con_DPrintf("DSP: failed to allocate pitch-shifter.\n");
2555 return NULL;
2556 }
2557
2558 pptc = &ptcs[i];
2559 PTC_Init( pptc );
2560
2561 // get size of region to cut or duplicate
2562 tcutdup = abs(( fstep - 1.0 ) * timeslice );
2563
2564 // to prevent buffer overruns:
2565
2566 // make sure timeslice is greater than cut/dup time
2567 tslice = max ( tslice, 1.1 * tcutdup);
2568
2569 // make sure xfade time smaller than cut/dup time, and smaller than (timeslice-cutdup) time
2570 txfade = min( txfade, 0.9 * tcutdup );
2571 txfade = min( txfade, 0.9 * ( tslice - tcutdup ));
2572
2573 pptc->cxfade = MSEC_TO_SAMPS( txfade );
2574 pptc->ccut = MSEC_TO_SAMPS( tcutdup );
2575 pptc->cduplicate = MSEC_TO_SAMPS( tcutdup );
2576
2577 // alloc delay lines (buffers)
2578 tout = tslice * fstep;
2579
2580 cin = MSEC_TO_SAMPS( tslice );
2581 cout = MSEC_TO_SAMPS( tout );
2582
2583 pptc->pdly_in = DLY_Alloc( cin, 0, 1, DLY_LINEAR ); // alloc input buffer
2584 pptc->pdly_out = DLY_Alloc( cout, 0, 1, DLY_LINEAR ); // alloc output buffer
2585
2586 if( !pptc->pdly_in || !pptc->pdly_out )
2587 {
2588 PTC_Free( pptc );
2589 Con_DPrintf("DSP: failed to allocate delay for pitch-shifter.\n");
2590 return NULL;
2591 }
2592
2593 // buffer pointers
2594 pptc->pin = pptc->pdly_in->w;
2595 pptc->pout = pptc->pdly_out->w;
2596
2597 // input buffer index
2598 pptc->iin = 0;
2599
2600 // output buffer index
2601 POS_ONE_Init( &pptc->psn, cout, fstep );
2602
2603 // if fstep > 1.0 we're pitching shifting up, so fdup = true
2604 pptc->fdup = fstep > 1.0 ? true : false;
2605
2606 pptc->cin = cin;
2607 pptc->cout = cout;
2608
2609 pptc->fstep = fstep;
2610 pptc->fused = true;
2611
2612 return pptc;
2613}
2614
2615// linear crossfader
2616// yfadein - instantaneous value fading in
2617// ydafeout -instantaneous value fading out
2618// nsamples - duration in #samples of fade
2619// isample - index in to fade 0...nsamples-1
2620inline int xfade( int yfadein, int yfadeout, int nsamples, int isample )
2621{
2622 int yout;
2623 int m = (isample << PBITS ) / nsamples;
2624
2625 yout = ((yfadein * m) >> PBITS) + ((yfadeout * (PMAX - m)) >> PBITS);
2626
2627 return yout;
2628}
2629
2630// w - pointer to start of input buffer samples
2631// v - pointer to start of output buffer samples
2632// cin - # of input buffer samples
2633// cout = # of output buffer samples
2634// cxfade = # of crossfade samples
2635// cduplicate = # of samples in duplicate/cut segment
2636void TimeExpand( int *w, int *v, int cin, int cout, int cxfade, int cduplicate )
2637{
2638 int i, j;
2639 int m;
2640 int p;
2641 int q;
2642 int D;
2643
2644 // input buffer
2645 // xfade source duplicate
2646 // [0...........][p.......n][m...........D]
2647
2648 // output buffer
2649 // xfade region duplicate
2650 // [0.....................n][m..[q.......D][m...........D]
2651
2652 // D - index of last sample in input buffer
2653 // m - index of 1st sample in duplication region
2654 // p - index of 1st sample of crossfade source
2655 // q - index of 1st sample in crossfade region
2656
2657 D = cin - 1;
2658 m = cin - cduplicate;
2659 p = m - cxfade;
2660 q = cin - cxfade;
2661
2662 // copy up to crossfade region
2663 for( i = 0; i < q; i++ )
2664 v[i] = w[i];
2665
2666 // crossfade region
2667 j = p;
2668
2669 for( i = q; i <= D; i++ )
2670 v[i] = xfade( w[j++], w[i], cxfade, i-q ); // fade out p..n, fade in q..D
2671
2672 // duplicate region
2673 j = D+1;
2674
2675 for( i = m; i <= D; i++ )
2676 v[j++] = w[i];
2677
2678}
2679
2680// cut ccut samples from end of input buffer, crossfade end of cut section
2681// with end of remaining section
2682
2683// w - pointer to start of input buffer samples
2684// v - pointer to start of output buffer samples
2685// cin - # of input buffer samples
2686// cout = # of output buffer samples
2687// cxfade = # of crossfade samples
2688// ccut = # of samples in cut segment
2689void TimeCompress( int *w, int *v, int cin, int cout, int cxfade, int ccut )
2690{
2691 int i, j;
2692 int m;
2693 int p;
2694 int q;
2695 int D;
2696
2697 // input buffer
2698 // xfade source
2699 // [0.....................n][m..[p.......D]
2700
2701 // xfade region cut
2702 // [0...........][q.......n][m...........D]
2703
2704 // output buffer
2705 // xfade to source
2706 // [0...........][p.......D]
2707
2708 // D - index of last sample in input buffer
2709 // m - index of 1st sample in cut region
2710 // p - index of 1st sample of crossfade source
2711 // q - index of 1st sample in crossfade region
2712
2713 D = cin - 1;
2714 m = cin - ccut;
2715 p = cin - cxfade;
2716 q = m - cxfade;
2717
2718 // copy up to crossfade region
2719
2720 for( i = 0; i < q; i++ )
2721 v[i] = w[i];
2722
2723 // crossfade region
2724 j = p;
2725
2726 for( i = q; i < m; i++ )
2727 v[i] = xfade( w[j++], w[i], cxfade, i-q ); // fade out p..n, fade in q..D
2728
2729 // skip rest of input buffer
2730}
2731
2732// get next sample
2733
2734// put input sample into input (delay) buffer
2735// get output sample from output buffer, step by fstep %
2736// output buffer is time expanded or compressed version of previous input buffer
2737inline int PTC_GetNext( ptc_t *pptc, int x )
2738{
2739 int iout, xout;
2740 qboolean fhitend = false;
2741
2742 // write x into input buffer
2743 ASSERT( pptc->iin < pptc->cin );
2744
2745 pptc->pin[pptc->iin] = x;
2746
2747 pptc->iin++;
2748
2749 // check for end of input buffer
2750 if( pptc->iin >= pptc->cin )
2751 fhitend = true;
2752
2753 // read sample from output buffer, resampling at fstep
2754 iout = POS_ONE_GetNext( &pptc->psn );
2755 ASSERT( iout < pptc->cout );
2756 xout = pptc->pout[iout];
2757
2758 if( fhitend )
2759 {
2760 // if hit end of input buffer (ie: input buffer is full)
2761 // reset input buffer pointer
2762 // reset output buffer pointer
2763 // rebuild entire output buffer (TimeCompress/TimeExpand)
2764
2765 pptc->iin = 0;
2766
2767 POS_ONE_Init( &pptc->psn, pptc->cout, pptc->fstep );
2768
2769 if( pptc->fdup ) TimeExpand ( pptc->pin, pptc->pout, pptc->cin, pptc->cout, pptc->cxfade, pptc->cduplicate );
2770 else TimeCompress ( pptc->pin, pptc->pout, pptc->cin, pptc->cout, pptc->cxfade, pptc->ccut );
2771 }
2772
2773 return xout;
2774}
2775
2776// batch version for performance
2777inline void PTC_GetNextN( ptc_t *pptc, portable_samplepair_t *pbuffer, int SampleCount, int op )
2778{
2779 int count = SampleCount;
2780 portable_samplepair_t *pb = pbuffer;
2781
2782 switch( op )
2783 {
2784 default:
2785 case OP_LEFT:
2786 while( count-- )
2787 {
2788 pb->left = PTC_GetNext( pptc, pb->left );
2789 pb++;
2790 }
2791 break;
2792 case OP_RIGHT:
2793 while( count-- )
2794 {
2795 pb->right = PTC_GetNext( pptc, pb->right );
2796 pb++;
2797 }
2798 break;
2799 case OP_LEFT_DUPLICATE:
2800 while( count-- )
2801 {
2802 pb->left = pb->right = PTC_GetNext( pptc, pb->left );
2803 pb++;
2804 }
2805 break;
2806 }
2807}
2808
2809// change time compression to new value
2810// fstep is new value
2811// ramptime is how long change takes in seconds (ramps smoothly), 0 for no ramp
2812
2813void PTC_ChangeVal( ptc_t *pptc, float fstep, float ramptime )
2814{
2815// UNDONE: ignored
2816// UNDONE: just realloc time compressor with new fstep
2817}
2818
2819// uses pitch:
2820// 1.0 = playback normal rate
2821// 0.5 = cut 50% of sound (2x playback)
2822// 1.5 = add 50% sound (0.5x playback)
2823
2824typedef enum
2825{
2826 // parameter order
2827 ptc_ipitch,
2828 ptc_itimeslice,
2829 ptc_ixfade,
2830 ptc_cparam // # of params
2831} ptc_e;
2832
2833// diffusor parameter ranges
2834prm_rng_t ptc_rng[] =
2835{
2836{ ptc_cparam, 0, 0 }, // first entry is # of parameters
2837{ ptc_ipitch, 0.1, 4.0 }, // 0-n.0 where 1.0 = 1 octave up and 0.5 is one octave down
2838{ ptc_itimeslice, 20.0, 300.0 }, // in milliseconds - size of sound chunk to analyze and cut/duplicate - 100ms nominal
2839{ ptc_ixfade, 1.0, 200.0 }, // in milliseconds - size of crossfade region between spliced chunks - 20ms nominal
2840};
2841
2842ptc_t *PTC_Params( prc_t *pprc )
2843{
2844 ptc_t *pptc;
2845
2846 float pitch = pprc->prm[ptc_ipitch];
2847 float timeslice = pprc->prm[ptc_itimeslice];
2848 float txfade = pprc->prm[ptc_ixfade];
2849
2850 pptc = PTC_Alloc( timeslice, txfade, pitch );
2851
2852 return pptc;
2853}
2854
2855inline void *PTC_VParams( void *p )
2856{
2857 PRC_CheckParams((prc_t *)p, ptc_rng );
2858 return (void *)PTC_Params((prc_t *)p);
2859}
2860
2861// change to new pitch value
2862// v is +/- 0-1.0
2863// v changes current pitch up/down by +/- v%
2864void PTC_Mod( ptc_t *pptc, float v )
2865{
2866 float fstep;
2867 float fstepnew;
2868
2869 fstep = pptc->fstep;
2870 fstepnew = fstep * (1.0 + v);
2871
2872 PTC_ChangeVal( pptc, fstepnew, 0.01 );
2873}
2874
2875
2876////////////////////
2877// ADSR envelope
2878////////////////////
2879
2880#define CENVS 64 // max # of envelopes active
2881#define CENVRMPS 4 // A, D, S, R
2882
2883#define ENV_LIN 0 // linear a,d,s,r
2884#define ENV_EXP 1 // exponential a,d,s,r
2885#define ENV_MAX ENV_EXP
2886
2887#define ENV_BITS 14 // bits of resolution of ramp
2888
2889typedef struct
2890{
2891 qboolean fused;
2892 qboolean fhitend; // true if done
2893 int ienv; // current ramp
2894 rmp_t rmps[CENVRMPS]; // ramps
2895} env_t;
2896
2897env_t envs[CENVS];
2898
2899void ENV_Init( env_t *penv ) { if( penv ) Q_memset( penv, 0, sizeof( env_t )); };
2900void ENV_Free( env_t *penv ) { if( penv ) Q_memset( penv, 0, sizeof( env_t )); };
2901void ENV_InitAll() { int i; for( i = 0; i < CENVS; i++ ) ENV_Init( &envs[i] ); };
2902void ENV_FreeAll() { int i; for( i = 0; i < CENVS; i++ ) ENV_Free( &envs[i] ); };
2903
2904
2905// allocate ADSR envelope
2906// all times are in seconds
2907// amp1 - attack amplitude multiplier 0-1.0
2908// amp2 - sustain amplitude multiplier 0-1.0
2909// amp3 - end of sustain amplitude multiplier 0-1.0
2910env_t *ENV_Alloc( int type, float famp1, float famp2, float famp3, float attack, float decay, float sustain, float release )
2911{
2912 int i;
2913 env_t *penv;
2914
2915 for( i = 0; i < CENVS; i++ )
2916 {
2917 if( !envs[i].fused )
2918 {
2919 int amp1 = famp1 * (1 << ENV_BITS); // ramp resolution
2920 int amp2 = famp2 * (1 << ENV_BITS);
2921 int amp3 = famp3 * (1 << ENV_BITS);
2922
2923 penv = &envs[i];
2924
2925 ENV_Init( penv );
2926
2927 // UNDONE: ignoring type = ENV_EXP - use oneshot LFOS instead with sawtooth/exponential
2928
2929 // set up ramps
2930 RMP_Init( &penv->rmps[0], attack, 0, amp1 );
2931 RMP_Init( &penv->rmps[1], decay, amp1, amp2 );
2932 RMP_Init( &penv->rmps[2], sustain, amp2, amp3 );
2933 RMP_Init( &penv->rmps[3], release, amp3, 0 );
2934
2935 penv->ienv = 0;
2936 penv->fused = true;
2937 penv->fhitend = false;
2938
2939 return penv;
2940 }
2941 }
2942
2943 Con_DPrintf("DSP: failed to allocate envelope.\n");
2944 return NULL;
2945}
2946
2947inline int ENV_GetNext( env_t *penv, int x )
2948{
2949 if( !penv->fhitend )
2950 {
2951 int i, y;
2952
2953 i = penv->ienv;
2954 y = RMP_GetNext( &penv->rmps[i] );
2955
2956 // check for next ramp
2957 if( penv->rmps[i].fhitend )
2958 i++;
2959
2960 penv->ienv = i;
2961
2962 // check for end of all ramps
2963 if( i > 3 ) penv->fhitend = true;
2964
2965 // multiply input signal by ramp
2966 return (x * y) >> ENV_BITS;
2967 }
2968 return 0;
2969}
2970
2971// batch version for performance
2972
2973inline void ENV_GetNextN( env_t *penv, portable_samplepair_t *pbuffer, int SampleCount, int op )
2974{
2975 int count = SampleCount;
2976 portable_samplepair_t *pb = pbuffer;
2977
2978 switch( op )
2979 {
2980 default:
2981 case OP_LEFT:
2982 while( count-- )
2983 {
2984 pb->left = ENV_GetNext( penv, pb->left );
2985 pb++;
2986 }
2987 break;
2988 case OP_RIGHT:
2989 while( count-- )
2990 {
2991 pb->right = ENV_GetNext( penv, pb->right );
2992 pb++;
2993 }
2994 break;
2995 case OP_LEFT_DUPLICATE:
2996 while( count-- )
2997 {
2998 pb->left = pb->right = ENV_GetNext( penv, pb->left );
2999 pb++;
3000 }
3001 break;
3002 }
3003}
3004
3005// uses lfowav, amp1, amp2, amp3, attack, decay, sustain, release
3006// lfowav is type, currently ignored - ie: LFO_LIN_IN, LFO_LOG_IN
3007
3008// parameter order
3009typedef enum
3010{
3011 env_itype,
3012 env_iamp1,
3013 env_iamp2,
3014 env_iamp3,
3015 env_iattack,
3016 env_idecay,
3017 env_isustain,
3018 env_irelease,
3019 env_cparam // # of params
3020
3021} env_e;
3022
3023// parameter ranges
3024prm_rng_t env_rng[] =
3025{
3026{ env_cparam, 0, 0 }, // first entry is # of parameters
3027{ env_itype, 0.0, ENV_MAX }, // ENV_LINEAR, ENV_LOG - currently ignored
3028{ env_iamp1, 0.0, 1.0 }, // attack peak amplitude 0-1.0
3029{ env_iamp2, 0.0, 1.0 }, // decay target amplitued 0-1.0
3030{ env_iamp3, 0.0, 1.0 }, // sustain target amplitude 0-1.0
3031{ env_iattack, 0.0, 20000.0 }, // attack time in milliseconds
3032{ env_idecay, 0.0, 20000.0 }, // envelope decay time in milliseconds
3033{ env_isustain, 0.0, 20000.0 }, // sustain time in milliseconds
3034{ env_irelease, 0.0, 20000.0 }, // release time in milliseconds
3035};
3036
3037env_t *ENV_Params( prc_t *pprc )
3038{
3039 env_t *penv;
3040
3041 float type = pprc->prm[env_itype];
3042 float amp1 = pprc->prm[env_iamp1];
3043 float amp2 = pprc->prm[env_iamp2];
3044 float amp3 = pprc->prm[env_iamp3];
3045 float attack = pprc->prm[env_iattack] / 1000.0f;
3046 float decay = pprc->prm[env_idecay] / 1000.0f;
3047 float sustain = pprc->prm[env_isustain] / 1000.0f;
3048 float release = pprc->prm[env_irelease] / 1000.0f;
3049
3050 penv = ENV_Alloc( type, amp1, amp2, amp3, attack, decay, sustain, release );
3051 return penv;
3052}
3053
3054inline void *ENV_VParams( void *p )
3055{
3056 PRC_CheckParams((prc_t *)p, env_rng );
3057 return (void *)ENV_Params((prc_t *)p);
3058}
3059
3060inline void ENV_Mod ( void *p, float v )
3061{
3062}
3063
3064////////////////////
3065// envelope follower
3066////////////////////
3067#define CEFOS 64 // max # of envelope followers active
3068
3069#define CEFOBITS 6 // size 2^6 = 64
3070#define CEFOWINDOW (1 << (CEFOBITS)) // size of sample window
3071
3072typedef struct
3073{
3074 qboolean fused;
3075 int avg; // accumulating average over sample window
3076 int cavg; // count down
3077 int xout; // current output value
3078
3079} efo_t;
3080
3081efo_t efos[CEFOS];
3082
3083void EFO_Init( efo_t *pefo ) { if( pefo ) Q_memset( pefo, 0, sizeof( efo_t )); };
3084void EFO_Free( efo_t *pefo ) { if( pefo ) Q_memset( pefo, 0, sizeof( efo_t )); };
3085void EFO_InitAll() { int i; for( i = 0; i < CEFOS; i++ ) EFO_Init( &efos[i] ); };
3086void EFO_FreeAll() { int i; for( i = 0; i < CEFOS; i++ ) EFO_Free( &efos[i] ); };
3087
3088// allocate enveloper follower
3089efo_t *EFO_Alloc( void )
3090{
3091 int i;
3092 efo_t *pefo;
3093
3094 for( i = 0; i < CEFOS; i++ )
3095 {
3096 if( !efos[i].fused )
3097 {
3098 pefo = &efos[i];
3099
3100 EFO_Init( pefo );
3101
3102 pefo->xout = 0;
3103 pefo->cavg = CEFOWINDOW;
3104 pefo->fused = true;
3105
3106 return pefo;
3107 }
3108 }
3109
3110 Con_DPrintf("DSP: failed to allocate envelope follower.\n");
3111 return NULL;
3112}
3113
3114
3115inline int EFO_GetNext( efo_t *pefo, int x )
3116{
3117 int xa = ABS( x ); // rectify input wav
3118
3119 // get running sum / 2
3120 pefo->avg += xa >> 1; // divide by 2 to prevent overflow
3121
3122 pefo->cavg--;
3123
3124 if( !pefo->cavg )
3125 {
3126 // new output value - end of window
3127
3128 // get average over window
3129 pefo->xout = pefo->avg >> (CEFOBITS - 1); // divide by window size / 2
3130 pefo->cavg = CEFOWINDOW;
3131 pefo->avg = 0;
3132 }
3133
3134 return pefo->xout;
3135}
3136
3137// batch version for performance
3138inline void EFO_GetNextN( efo_t *pefo, portable_samplepair_t *pbuffer, int SampleCount, int op )
3139{
3140 int count = SampleCount;
3141 portable_samplepair_t *pb = pbuffer;
3142
3143 switch( op )
3144 {
3145 default:
3146 case OP_LEFT:
3147 while( count-- )
3148 {
3149 pb->left = EFO_GetNext( pefo, pb->left );
3150 pb++;
3151 }
3152 break;
3153 case OP_RIGHT:
3154 while( count-- )
3155 {
3156 pb->right = EFO_GetNext( pefo, pb->right );
3157 pb++;
3158 }
3159 break;
3160 case OP_LEFT_DUPLICATE:
3161 while( count-- )
3162 {
3163 pb->left = pb->right = EFO_GetNext( pefo, pb->left );
3164 pb++;
3165 }
3166 break;
3167 }
3168}
3169
3170
3171efo_t * EFO_Params( prc_t *pprc )
3172{
3173 return EFO_Alloc();
3174}
3175
3176inline void *EFO_VParams( void *p )
3177{
3178 // PRC_CheckParams(( prc_t *)p, efo_rng ); - efo has no params
3179 return (void *)EFO_Params((prc_t *)p );
3180}
3181
3182inline void EFO_Mod( void *p, float v )
3183{
3184}
3185
3186//////////////
3187// mod delay
3188//////////////
3189
3190// modulate delay time anywhere from 0..D using MDY_ChangeVal. no output glitches (uses RMP)
3191
3192#define CMDYS 64 // max # of mod delays active (steals from delays)
3193
3194typedef struct
3195{
3196 qboolean fused;
3197 qboolean fchanging; // true if modulating to new delay value
3198 dly_t *pdly; // delay
3199 int Dcur; // current delay value
3200 float ramptime; // ramp 'glide' time - time in seconds to change between values
3201 int mtime; // time in samples between delay changes. 0 implies no self-modulating
3202 int mtimecur; // current time in samples until next delay change
3203 float depth; // modulate delay from D to D - (D*depth) depth 0-1.0
3204 int xprev; // previous delay output, used to smooth transitions between delays
3205 rmp_t rmp; // ramp
3206} mdy_t;
3207
3208mdy_t mdys[CMDYS];
3209
3210void MDY_Init( mdy_t *pmdy ) { if( pmdy ) Q_memset( pmdy, 0, sizeof( mdy_t )); };
3211void MDY_Free( mdy_t *pmdy ) { if( pmdy ) { DLY_Free( pmdy->pdly ); Q_memset( pmdy, 0, sizeof( mdy_t )); } };
3212void MDY_InitAll() { int i; for( i = 0; i < CMDYS; i++ ) MDY_Init( &mdys[i] ); };
3213void MDY_FreeAll() { int i; for( i = 0; i < CMDYS; i++ ) MDY_Free( &mdys[i] ); };
3214
3215
3216// allocate mod delay, given previously allocated dly
3217// ramptime is time in seconds for delay to change from dcur to dnew
3218// modtime is time in seconds between modulations. 0 if no self-modulation
3219// depth is 0-1.0 multiplier, new delay values when modulating are Dnew = randomlong (D - D*depth, D)
3220mdy_t *MDY_Alloc( dly_t *pdly, float ramptime, float modtime, float depth )
3221{
3222 int i;
3223 mdy_t *pmdy;
3224
3225 if( !pdly )
3226 return NULL;
3227
3228 for( i = 0; i < CMDYS; i++ )
3229 {
3230 if( !mdys[i].fused )
3231 {
3232 pmdy = &mdys[i];
3233
3234 MDY_Init( pmdy );
3235
3236 pmdy->pdly = pdly;
3237
3238 if( !pmdy->pdly )
3239 {
3240 Con_DPrintf("DSP: failed to allocate delay for mod-delay.\n");
3241 return NULL;
3242 }
3243
3244 pmdy->Dcur = pdly->D0;
3245 pmdy->fused = true;
3246 pmdy->ramptime = ramptime;
3247 pmdy->mtime = SEC_TO_SAMPS( modtime );
3248 pmdy->mtimecur = pmdy->mtime;
3249 pmdy->depth = depth;
3250
3251 return pmdy;
3252 }
3253 }
3254
3255 Con_DPrintf("DSP: failed to allocate mod-delay.\n");
3256 return NULL;
3257}
3258
3259// change to new delay tap value t samples, ramp linearly over ramptime seconds
3260void MDY_ChangeVal( mdy_t *pmdy, int t )
3261{
3262 // if D > original delay value, cap at original value
3263
3264 t = min( pmdy->pdly->D0, t );
3265 pmdy->fchanging = true;
3266
3267 RMP_Init( &pmdy->rmp, pmdy->ramptime, pmdy->Dcur, t );
3268}
3269
3270// get next value from modulating delay
3271int MDY_GetNext( mdy_t *pmdy, int x )
3272{
3273 int xout;
3274 int xcur;
3275
3276 // get current delay output
3277 xcur = DLY_GetNext( pmdy->pdly, x );
3278
3279 // return right away if not modulating (not changing and not self modulating)
3280 if( !pmdy->fchanging && !pmdy->mtime )
3281 {
3282 pmdy->xprev = xcur;
3283 return xcur;
3284 }
3285
3286 xout = xcur;
3287
3288 // if currently changing to new delay target, get next delay value
3289 if( pmdy->fchanging )
3290 {
3291 // get next ramp value, test for done
3292 int r = RMP_GetNext( &pmdy->rmp );
3293
3294 if( RMP_HitEnd( &pmdy->rmp ))
3295 pmdy->fchanging = false;
3296
3297 // if new delay different from current delay, change delay
3298 if( r != pmdy->Dcur )
3299 {
3300 // ramp never changes by more than + or - 1
3301
3302 // change delay tap value to r
3303 DLY_ChangeVal( pmdy->pdly, r );
3304
3305 pmdy->Dcur = r;
3306
3307 // filter delay output within transitions.
3308 // note: xprev = xcur = 0 if changing delay on 1st sample
3309 xout = ( xcur + pmdy->xprev ) >> 1;
3310 }
3311 }
3312
3313 // if self-modulating and timer has expired, get next change
3314 if( pmdy->mtime && !pmdy->mtimecur-- )
3315 {
3316 int D0 = pmdy->pdly->D0;
3317 int Dnew;
3318 float D1;
3319
3320 pmdy->mtimecur = pmdy->mtime;
3321
3322 // modulate between 0 and 100% of d0
3323 D1 = (float)D0 * (1.0 - pmdy->depth);
3324 Dnew = Com_RandomLong( (int)D1, D0 );
3325
3326 MDY_ChangeVal( pmdy, Dnew );
3327 }
3328
3329 pmdy->xprev = xcur;
3330
3331 return xout;
3332}
3333
3334// batch version for performance
3335inline void MDY_GetNextN( mdy_t *pmdy, portable_samplepair_t *pbuffer, int SampleCount, int op )
3336{
3337 int count = SampleCount;
3338 portable_samplepair_t *pb = pbuffer;
3339
3340 switch( op )
3341 {
3342 default:
3343 case OP_LEFT:
3344 while( count-- )
3345 {
3346 pb->left = MDY_GetNext( pmdy, pb->left );
3347 pb++;
3348 }
3349 return;
3350 case OP_RIGHT:
3351 while( count-- )
3352 {
3353 pb->right = MDY_GetNext( pmdy, pb->right );
3354 pb++;
3355 }
3356 return;
3357 case OP_LEFT_DUPLICATE:
3358 while( count-- )
3359 {
3360 pb->left = pb->right = MDY_GetNext( pmdy, pb->left );
3361 pb++;
3362 }
3363 return;
3364 }
3365}
3366
3367// parameter order
3368typedef enum
3369{
3370 mdy_idtype, // NOTE: first 8 params must match params in dly_e
3371 mdy_idelay,
3372 mdy_ifeedback,
3373 mdy_igain,
3374 mdy_iftype,
3375 mdy_icutoff,
3376 mdy_iqwidth,
3377 mdy_iquality,
3378 mdy_imodrate,
3379 mdy_imoddepth,
3380 mdy_imodglide,
3381 mdy_cparam
3382} mdy_e;
3383
3384
3385// parameter ranges
3386prm_rng_t mdy_rng[] =
3387{
3388{ mdy_cparam, 0, 0 }, // first entry is # of parameters
3389
3390// delay params
3391{ mdy_idtype, 0, DLY_MAX }, // delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS
3392{ mdy_idelay, 0.0, 1000.0 }, // delay in milliseconds
3393{ mdy_ifeedback, 0.0, 0.99 }, // feedback 0-1.0
3394{ mdy_igain, 0.0, 1.0 }, // final gain of output stage, 0-1.0
3395
3396// filter params if mdy type DLY_LOWPASS
3397{ mdy_iftype, 0, FTR_MAX },
3398{ mdy_icutoff, 10.0, 22050.0 },
3399{ mdy_iqwidth, 100.0, 11025.0 },
3400{ mdy_iquality, 0, QUA_MAX },
3401{ mdy_imodrate, 0.01, 200.0 }, // frequency at which delay values change to new random value. 0 is no self-modulation
3402{ mdy_imoddepth, 0.0, 1.0 }, // how much delay changes (decreases) from current value (0-1.0)
3403{ mdy_imodglide, 0.01, 100.0 }, // glide time between dcur and dnew in milliseconds
3404};
3405
3406// convert user parameters to internal parameters, allocate and return
3407mdy_t *MDY_Params( prc_t *pprc )
3408{
3409 mdy_t *pmdy;
3410 dly_t *pdly;
3411
3412 float ramptime = pprc->prm[mdy_imodglide] / 1000.0; // get ramp time in seconds
3413 float modtime = 1.0 / pprc->prm[mdy_imodrate]; // time between modulations in seconds
3414 float depth = pprc->prm[mdy_imoddepth]; // depth of modulations 0-1.0
3415
3416 // alloc plain, allpass or lowpass delay
3417 pdly = DLY_Params( pprc );
3418 if( !pdly ) return NULL;
3419
3420 pmdy = MDY_Alloc( pdly, ramptime, modtime, depth );
3421
3422 return pmdy;
3423}
3424
3425inline void * MDY_VParams( void *p )
3426{
3427 PRC_CheckParams(( prc_t *)p, mdy_rng );
3428 return (void *)MDY_Params ((prc_t *)p );
3429}
3430
3431// v is +/- 0-1.0
3432// change current delay value 0..D
3433void MDY_Mod( mdy_t *pmdy, float v )
3434{
3435 int D = pmdy->Dcur;
3436 float v2 = -(v + 1.0)/2.0; // v2 varies -1.0-0.0
3437
3438 // D varies 0..D
3439 D = D + (int)((float)D * v2);
3440
3441 // change delay
3442 MDY_ChangeVal( pmdy, D );
3443}
3444
3445
3446///////////////////////////////////////////
3447// Chorus - lfo modulated delay
3448///////////////////////////////////////////
3449
3450
3451#define CCRSS 64 // max number chorus' active
3452
3453typedef struct
3454{
3455 qboolean fused;
3456 mdy_t *pmdy; // modulatable delay
3457 lfo_t *plfo; // modulating lfo
3458 int lfoprev; // previous modulator value from lfo
3459 int mix; // mix of clean & chorus signal - 0..PMAX
3460} crs_t;
3461
3462crs_t crss[CCRSS];
3463
3464void CRS_Init( crs_t *pcrs ) { if( pcrs ) Q_memset( pcrs, 0, sizeof( crs_t )); };
3465void CRS_Free( crs_t *pcrs )
3466{
3467 if( pcrs )
3468 {
3469 MDY_Free( pcrs->pmdy );
3470 LFO_Free( pcrs->plfo );
3471 Q_memset( pcrs, 0, sizeof( crs_t ));
3472 }
3473}
3474
3475void CRS_InitAll() { int i; for( i = 0; i < CCRSS; i++ ) CRS_Init( &crss[i] ); }
3476void CRS_FreeAll() { int i; for( i = 0; i < CCRSS; i++ ) CRS_Free( &crss[i] ); }
3477
3478// fstep is base pitch shift, ie: floating point step value, where 1.0 = +1 octave, 0.5 = -1 octave
3479// lfotype is LFO_SIN, LFO_RND, LFO_TRI etc (LFO_RND for chorus, LFO_SIN for flange)
3480// fHz is modulation frequency in Hz
3481// depth is modulation depth, 0-1.0
3482// mix is mix of chorus and clean signal
3483
3484#define CRS_DELAYMAX 100 // max milliseconds of sweepable delay
3485#define CRS_RAMPTIME 5 // milliseconds to ramp between new delay values
3486
3487crs_t * CRS_Alloc( int lfotype, float fHz, float fdepth, float mix )
3488{
3489 int i, D;
3490 crs_t *pcrs;
3491 dly_t *pdly;
3492 mdy_t *pmdy;
3493 lfo_t *plfo;
3494 float ramptime;
3495
3496 // find free chorus slot
3497 for( i = 0; i < CCRSS; i++ )
3498 {
3499 if( !crss[i].fused )
3500 break;
3501 }
3502
3503 if( i == CCRSS )
3504 {
3505 Con_DPrintf("DSP: failed to allocate chorus.\n");
3506 return NULL;
3507 }
3508
3509 pcrs = &crss[i];
3510 CRS_Init( pcrs );
3511
3512 D = fdepth * MSEC_TO_SAMPS( CRS_DELAYMAX ); // sweep from 0 - n milliseconds
3513
3514 ramptime = (float)CRS_RAMPTIME / 1000.0f; // # milliseconds to ramp between new values
3515
3516 pdly = DLY_Alloc( D, 0, 1, DLY_LINEAR );
3517 pmdy = MDY_Alloc( pdly, ramptime, 0.0, 0.0 );
3518 plfo = LFO_Alloc( lfotype, fHz, false );
3519
3520 if( !plfo || !pmdy )
3521 {
3522 LFO_Free( plfo );
3523 MDY_Free( pmdy );
3524 Con_DPrintf("DSP: failed to allocate lfo or mdy for chorus.\n");
3525 return NULL;
3526 }
3527
3528 pcrs->pmdy = pmdy;
3529 pcrs->plfo = plfo;
3530 pcrs->mix = (int)( PMAX * mix );
3531 pcrs->fused = true;
3532
3533 return pcrs;
3534}
3535
3536// return next chorused sample (modulated delay) mixed with input sample
3537inline int CRS_GetNext( crs_t *pcrs, int x )
3538{
3539 int l, y;
3540
3541 // get current mod delay value
3542 y = MDY_GetNext( pcrs->pmdy, x );
3543
3544 // get next lfo value for modulation
3545 // note: lfo must return 0 as first value
3546 l = LFO_GetNext( pcrs->plfo, x );
3547
3548 // if modulator has changed, change mdy
3549 if( l != pcrs->lfoprev )
3550 {
3551 // calculate new tap (starts at D)
3552 int D = pcrs->pmdy->pdly->D0;
3553 int tap;
3554
3555 // lfo should always output values 0 <= l <= LFOMAX
3556
3557 if( l < 0 ) l = 0;
3558
3559 tap = D - ((l * D) >> LFOBITS);
3560 MDY_ChangeVal ( pcrs->pmdy, tap );
3561 pcrs->lfoprev = l;
3562 }
3563
3564 return ((y * pcrs->mix) >> PBITS) + x;
3565}
3566
3567// batch version for performance
3568inline void CRS_GetNextN( crs_t *pcrs, portable_samplepair_t *pbuffer, int SampleCount, int op )
3569{
3570 int count = SampleCount;
3571 portable_samplepair_t *pb = pbuffer;
3572
3573 switch( op )
3574 {
3575 default:
3576 case OP_LEFT:
3577 while( count-- )
3578 {
3579 pb->left = CRS_GetNext( pcrs, pb->left );
3580 pb++;
3581 }
3582 break;
3583 case OP_RIGHT:
3584 while( count-- )
3585 {
3586 pb->right = CRS_GetNext( pcrs, pb->right );
3587 pb++;
3588 }
3589 break;
3590 case OP_LEFT_DUPLICATE:
3591 while( count-- )
3592 {
3593 pb->left = pb->right = CRS_GetNext( pcrs, pb->left );
3594 pb++;
3595 }
3596 break;
3597 }
3598}
3599
3600// parameter order
3601typedef enum
3602{
3603 crs_ilfotype,
3604 crs_irate,
3605 crs_idepth,
3606 crs_imix,
3607 crs_cparam
3608} crs_e;
3609
3610
3611// parameter ranges
3612prm_rng_t crs_rng[] =
3613{
3614{ crs_cparam, 0, 0 }, // first entry is # of parameters
3615{ crs_ilfotype, 0, LFO_MAX }, // lfotype is LFO_SIN, LFO_RND, LFO_TRI etc (LFO_RND for chorus, LFO_SIN for flange)
3616{ crs_irate, 0.0, 1000.0 }, // rate is modulation frequency in Hz
3617{ crs_idepth, 0.0, 1.0 }, // depth is modulation depth, 0-1.0
3618{ crs_imix, 0.0, 1.0 }, // mix is mix of chorus and clean signal
3619};
3620
3621// uses pitch, lfowav, rate, depth
3622crs_t *CRS_Params( prc_t *pprc )
3623{
3624 crs_t *pcrs;
3625
3626 pcrs = CRS_Alloc( pprc->prm[crs_ilfotype], pprc->prm[crs_irate], pprc->prm[crs_idepth], pprc->prm[crs_imix] );
3627
3628 return pcrs;
3629}
3630
3631inline void *CRS_VParams( void *p )
3632{
3633 PRC_CheckParams((prc_t *)p, crs_rng );
3634 return (void *)CRS_Params((prc_t *)p );
3635}
3636
3637inline void CRS_Mod( void *p, float v )
3638{
3639}
3640
3641////////////////////////////////////////////////////
3642// amplifier - modulatable gain, distortion
3643////////////////////////////////////////////////////
3644
3645#define CAMPS 64 // max number amps active
3646#define AMPSLEW 10 // milliseconds of slew time between gain changes
3647
3648typedef struct
3649{
3650 qboolean fused;
3651 float gain; // amplification 0-6.0
3652 float vthresh; // clip distortion threshold 0-1.0
3653 float distmix; // 0-1.0 mix of distortion with clean
3654 float vfeed; // 0-1.0 feedback with distortion;
3655 float gaintarget; // new gain
3656 float gaindif; // incrementer
3657} amp_t;
3658
3659amp_t amps[CAMPS];
3660
3661void AMP_Init( amp_t *pamp ) { if( pamp ) Q_memset( pamp, 0, sizeof( amp_t )); }
3662void AMP_Free( amp_t *pamp ) { if( pamp ) Q_memset( pamp, 0, sizeof( amp_t )); }
3663void AMP_InitAll() { int i; for( i = 0; i < CAMPS; i++ ) AMP_Init( &s[i] ); }
3664void AMP_FreeAll() { int i; for( i = 0; i < CAMPS; i++ ) AMP_Free( &s[i] ); }
3665
3666amp_t *AMP_Alloc( float gain, float vthresh, float distmix, float vfeed )
3667{
3668 int i;
3669 amp_t *pamp;
3670
3671 // find free amp slot
3672 for( i = 0; i < CAMPS; i++ )
3673 {
3674 if ( !amps[i].fused )
3675 break;
3676 }
3677
3678 if( i == CAMPS )
3679 {
3680 Con_DPrintf("DSP: failed to allocate amp.\n");
3681 return NULL;
3682 }
3683
3684 pamp = &s[i];
3685
3686 AMP_Init ( pamp );
3687
3688 pamp->gain = gain;
3689 pamp->vthresh = vthresh;
3690 pamp->distmix = distmix;
3691 pamp->vfeed = vfeed;
3692
3693 return pamp;
3694}
3695
3696// return next amplified sample
3697inline int AMP_GetNext( amp_t *pamp, int x )
3698{
3699 float y = (float)x;
3700 float yin;
3701 float gain = pamp->gain;
3702
3703 yin = y;
3704
3705 // slew between gains
3706 if( gain != pamp->gaintarget )
3707 {
3708 float gaintarget = pamp->gaintarget;
3709 float gaindif = pamp->gaindif;
3710
3711 if( gain > gaintarget )
3712 {
3713 gain -= gaindif;
3714 if( gain <= gaintarget )
3715 pamp->gaintarget = gain;
3716 }
3717 else
3718 {
3719 gain += gaindif;
3720 if( gain >= gaintarget )
3721 pamp->gaintarget = gain;
3722 }
3723
3724 pamp->gain = gain;
3725 }
3726
3727 // if distortion is on, add distortion, feedback
3728 if( pamp->vthresh < 1.0 )
3729 {
3730 float fclip = pamp->vthresh * 32767.0;
3731
3732 if( pamp->vfeed > 0.0 )
3733 {
3734 // UNDONE: feedback
3735 }
3736
3737 // clip distort
3738 y = ( y > fclip ? fclip : ( y < -fclip ? -fclip : y));
3739
3740 // mix distorted with clean (1.0 = full distortion)
3741 if( pamp->distmix > 0.0 )
3742 y = y * pamp->distmix + yin * (1.0 - pamp->distmix);
3743 }
3744
3745 // amplify
3746 y *= gain;
3747
3748 return (int)y;
3749}
3750
3751// batch version for performance
3752inline void AMP_GetNextN( amp_t *pamp, portable_samplepair_t *pbuffer, int SampleCount, int op )
3753{
3754 int count = SampleCount;
3755 portable_samplepair_t *pb = pbuffer;
3756
3757 switch( op )
3758 {
3759 default:
3760 case OP_LEFT:
3761 while( count-- )
3762 {
3763 pb->left = AMP_GetNext( pamp, pb->left );
3764 pb++;
3765 }
3766 break;
3767 case OP_RIGHT:
3768 while( count-- )
3769 {
3770 pb->right = AMP_GetNext( pamp, pb->right );
3771 pb++;
3772 }
3773 break;
3774 case OP_LEFT_DUPLICATE:
3775 while( count-- )
3776 {
3777 pb->left = pb->right = AMP_GetNext( pamp, pb->left );
3778 pb++;
3779 }
3780 break;
3781 }
3782}
3783
3784inline void AMP_Mod( amp_t *pamp, float v )
3785{
3786 float vmod = bound( v, 0.0, 1.0 );
3787 float samps = MSEC_TO_SAMPS( AMPSLEW ); // # samples to slew between amp values
3788
3789 // ramp to new amplification value
3790 pamp->gaintarget = pamp->gain * vmod;
3791
3792 pamp->gaindif = fabs( pamp->gain - pamp->gaintarget ) / samps;
3793
3794 if( pamp->gaindif == 0.0f )
3795 pamp->gaindif = fabs( pamp->gain - pamp->gaintarget ) / 100;
3796}
3797
3798
3799// parameter order
3800typedef enum
3801{
3802 amp_gain,
3803 amp_vthresh,
3804 amp_distmix,
3805 amp_vfeed,
3806 amp_cparam
3807} amp_e;
3808
3809
3810// parameter ranges
3811prm_rng_t amp_rng[] =
3812{
3813{ amp_cparam, 0, 0 }, // first entry is # of parameters
3814{ amp_gain, 0.0, 10.0 }, // amplification
3815{ amp_vthresh, 0.0, 1.0 }, // threshold for distortion (1.0 = no distortion)
3816{ amp_distmix, 0.0, 1.0 }, // mix of clean and distortion (1.0 = full distortion, 0.0 = full clean)
3817{ amp_vfeed, 0.0, 1.0 }, // distortion feedback
3818};
3819
3820amp_t * AMP_Params( prc_t *pprc )
3821{
3822 amp_t *pamp;
3823
3824 pamp = AMP_Alloc( pprc->prm[amp_gain], pprc->prm[amp_vthresh], pprc->prm[amp_distmix], pprc->prm[amp_vfeed] );
3825
3826 return pamp;
3827}
3828
3829inline void *AMP_VParams( void *p )
3830{
3831 PRC_CheckParams((prc_t *)p, amp_rng );
3832 return (void *)AMP_Params((prc_t *)p );
3833}
3834
3835
3836/////////////////
3837// NULL processor
3838/////////////////
3839typedef struct
3840{
3841 int type;
3842} nul_t;
3843
3844nul_t nuls[] = { 0 };
3845
3846void NULL_Init( nul_t *pnul ) { }
3847void NULL_InitAll( ) { }
3848void NULL_Free( nul_t *pnul ) { }
3849void NULL_FreeAll( ) { }
3850nul_t *NULL_Alloc( ) { return &nuls[0]; }
3851
3852inline int NULL_GetNext( void *p, int x ) { return x; }
3853inline void NULL_GetNextN( nul_t *pnul, portable_samplepair_t *pbuffer, int SampleCount, int op ) { return; }
3854inline void NULL_Mod( void *p, float v ) { return; }
3855inline void * NULL_VParams( void *p ) { return (void *)(&nuls[0]); }
3856
3857//////////////////////////
3858// DSP processors presets
3859//////////////////////////
3860
3861// A dsp processor (prc) performs a single-sample function, such as pitch shift, delay, reverb, filter
3862
3863// note, this array must have CPRCPARMS entries
3864#define PRMZERO 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
3865#define PFNZERO NULL,NULL,NULL,NULL,NULL // zero pointers for pfnparam...pdata within prc_t
3866
3867//////////////////
3868// NULL processor
3869/////////////////
3870
3871#define PRC_NULL1 { PRC_NULL, PRMZERO, PFNZERO }
3872
3873#define PRC0 PRC_NULL1
3874
3875//////////////
3876// Amplifiers
3877//////////////
3878
3879// {amp_gain, 0.0, 10.0 }, // amplification
3880// {amp_vthresh, 0.0, 1.0 }, // threshold for distortion (1.0 = no distortion)
3881// {amp_distmix, 0.0, 1.0 }, // mix of clean and distortion (1.0 = full distortion, 0.0 = full clean)
3882// {amp_vfeed, 0.0, 1.0 }, // distortion feedback
3883
3884// prctype gain vthresh distmix vfeed
3885#define PRC_AMP1 {PRC_AMP, { 1.0, 1.0, 0.0, 0.0, }, PFNZERO } // modulatable unity gain amp
3886#define PRC_AMP2 {PRC_AMP, { 1.5, 0.75, 1.0, 0.0, }, PFNZERO } // amp with light distortion
3887#define PRC_AMP3 {PRC_AMP, { 2.0, 0.5, 1.0, 0.0, }, PFNZERO } // amp with medium distortion
3888#define PRC_AMP4 {PRC_AMP, { 4.0, 0.25, 1.0, 0.0, }, PFNZERO } // amp with heavy distortion
3889#define PRC_AMP5 {PRC_AMP, { 10.0, 0.10, 1.0, 0.0, }, PFNZERO } // mega distortion
3890
3891#define PRC_AMP6 {PRC_AMP, { 0.1, 1.0, 0.0, 0.0, }, PFNZERO } // fade out
3892#define PRC_AMP7 {PRC_AMP, { 0.2, 1.0, 0.0, 0.0, }, PFNZERO } // fade out
3893#define PRC_AMP8 {PRC_AMP, { 0.3, 1.0, 0.0, 0.0, }, PFNZERO } // fade out
3894
3895#define PRC_AMP9 {PRC_AMP, { 0.75, 1.0, 0.0, 0.0, }, PFNZERO } // duck out
3896
3897
3898///////////
3899// Filters
3900///////////
3901
3902// ftype: filter type FLT_LP, FLT_HP, FLT_BP (UNDONE: FLT_BP currently ignored)
3903// cutoff: cutoff frequency in hz at -3db gain
3904// qwidth: width of BP, or steepness of LP/HP (ie: fcutoff + qwidth = -60db gain point)
3905// quality: QUA_LO, _MED, _HI 0,1,2
3906
3907// prctype ftype cutoff qwidth quality
3908#define PRC_FLT1 {PRC_FLT, { FLT_LP, 3000, 1000, QUA_MED, }, PFNZERO }
3909#define PRC_FLT2 {PRC_FLT, { FLT_LP, 2000, 2000, QUA_MED, }, PFNZERO } // lowpass for facing away
3910#define PRC_FLT3 {PRC_FLT, { FLT_LP, 1000, 1000, QUA_MED, }, PFNZERO }
3911#define PRC_FLT4 {PRC_FLT, { FLT_LP, 700, 700, QUA_LO, }, PFNZERO } // muffle filter
3912
3913#define PRC_FLT5 {PRC_FLT, { FLT_HP, 700, 200, QUA_MED, }, PFNZERO } // highpass (bandpass pair)
3914#define PRC_FLT6 {PRC_FLT, { FLT_HP, 2000, 1000, QUA_MED, }, PFNZERO } // lowpass (bandpass pair)
3915
3916//////////
3917// Delays
3918//////////
3919
3920// dtype: delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS
3921// delay: delay in milliseconds
3922// feedback: feedback 0-1.0
3923// gain: final gain of output stage, 0-1.0
3924
3925// prctype dtype delay feedbk gain ftype cutoff qwidth quality
3926#define PRC_DLY1 {PRC_DLY, { DLY_PLAIN, 500.0, 0.5, 0.6, 0.0, 0.0, 0.0, 0.0, }, PFNZERO }
3927#define PRC_DLY2 {PRC_DLY, { DLY_LOWPASS, 45.0, 0.8, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO }
3928#define PRC_DLY3 {PRC_DLY, { DLY_LOWPASS, 300.0, 0.5, 0.6, FLT_LP, 2000, 2000, QUA_LO, }, PFNZERO } // outside S
3929#define PRC_DLY4 {PRC_DLY, { DLY_LOWPASS, 400.0, 0.5, 0.6, FLT_LP, 1500, 1500, QUA_LO, }, PFNZERO } // outside M
3930#define PRC_DLY5 {PRC_DLY, { DLY_LOWPASS, 750.0, 0.5, 0.6, FLT_LP, 1000, 1000, QUA_LO, }, PFNZERO } // outside L
3931#define PRC_DLY6 {PRC_DLY, { DLY_LOWPASS, 1000.0, 0.5, 0.6, FLT_LP, 800, 400, QUA_LO, }, PFNZERO } // outside VL
3932#define PRC_DLY7 {PRC_DLY, { DLY_LOWPASS, 45.0, 0.4, 0.5, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // tunnel S
3933#define PRC_DLY8 {PRC_DLY, { DLY_LOWPASS, 55.0, 0.4, 0.5, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // tunnel M
3934#define PRC_DLY9 {PRC_DLY, { DLY_LOWPASS, 65.0, 0.4, 0.5, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // tunnel L
3935#define PRC_DLY10 {PRC_DLY, { DLY_LOWPASS, 150.0, 0.5, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // cavern S
3936#define PRC_DLY11 {PRC_DLY, { DLY_LOWPASS, 200.0, 0.7, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // cavern M
3937#define PRC_DLY12 {PRC_DLY, { DLY_LOWPASS, 300.0, 0.7, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // cavern L
3938#define PRC_DLY13 {PRC_DLY, { DLY_LINEAR, 300.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,}, PFNZERO } // straight delay 300ms
3939#define PRC_DLY14 {PRC_DLY, { DLY_LINEAR, 80.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,}, PFNZERO } // straight delay 80ms
3940
3941///////////
3942// Reverbs
3943///////////
3944
3945// size: 0-2.0 scales nominal delay parameters (starting at approx 20ms)
3946// density: 0-2.0 density of reverbs (room shape) - controls # of parallel or series delays
3947// decay: 0-2.0 scales feedback parameters (starting at approx 0.15)
3948
3949// prctype size density decay ftype cutoff qwidth fparallel
3950#define PRC_RVA1 {PRC_RVA, {2.0, 0.5, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO }
3951#define PRC_RVA2 {PRC_RVA, {1.0, 0.2, 1.5, 0, 0, 0, 0}, PFNZERO }
3952
3953#define PRC_RVA3 {PRC_RVA, {0.8, 0.5, 1.5, FLT_LP, 2500, 2000, 0}, PFNZERO } // metallic S
3954#define PRC_RVA4 {PRC_RVA, {1.0, 0.5, 1.5, FLT_LP, 2500, 2000, 0}, PFNZERO } // metallic M
3955#define PRC_RVA5 {PRC_RVA, {1.2, 0.5, 1.5, FLT_LP, 2500, 2000, 0}, PFNZERO } // metallic L
3956
3957#define PRC_RVA6 {PRC_RVA, {0.8, 0.3, 1.5, FLT_LP, 4000, 2000, 0}, PFNZERO } // tunnel S
3958#define PRC_RVA7 {PRC_RVA, {0.9, 0.3, 1.5, FLT_LP, 4000, 2000, 0}, PFNZERO } // tunnel M
3959#define PRC_RVA8 {PRC_RVA, {1.0, 0.3, 1.5, FLT_LP, 4000, 2000, 0}, PFNZERO } // tunnel L
3960
3961#define PRC_RVA9 {PRC_RVA, {2.0, 1.5, 2.0, FLT_LP, 1500, 1500, 1}, PFNZERO } // cavern S
3962#define PRC_RVA10 {PRC_RVA, {2.0, 1.5, 2.0, FLT_LP, 1500, 1500, 1}, PFNZERO } // cavern M
3963#define PRC_RVA11 {PRC_RVA, {2.0, 1.5, 2.0, FLT_LP, 1500, 1500, 1}, PFNZERO } // cavern L
3964
3965#define PRC_RVA12 {PRC_RVA, {2.0, 0.5, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } // chamber S
3966#define PRC_RVA13 {PRC_RVA, {2.0, 1.0, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } // chamber M
3967#define PRC_RVA14 {PRC_RVA, {2.0, 2.0, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } // chamber L
3968
3969#define PRC_RVA15 {PRC_RVA, {1.7, 1.0, 1.2, FLT_LP, 5000, 4000, 1}, PFNZERO } // brite S
3970#define PRC_RVA16 {PRC_RVA, {1.75, 1.0, 1.5, FLT_LP, 5000, 4000, 1}, PFNZERO } // brite M
3971#define PRC_RVA17 {PRC_RVA, {1.85, 1.0, 2.0, FLT_LP, 6000, 4000, 1}, PFNZERO } // brite L
3972
3973#define PRC_RVA18 {PRC_RVA, {1.0, 1.5, 1.0, FLT_LP, 1000, 1000, 0}, PFNZERO } // generic
3974
3975#define PRC_RVA19 {PRC_RVA, {1.9, 1.8, 1.25, FLT_LP, 4000, 2000, 1}, PFNZERO } // concrete S
3976#define PRC_RVA20 {PRC_RVA, {2.0, 1.8, 1.5, FLT_LP, 3500, 2000, 1}, PFNZERO } // concrete M
3977#define PRC_RVA21 {PRC_RVA, {2.0, 1.8, 1.75, FLT_LP, 3000, 2000, 1}, PFNZERO } // concrete L
3978
3979#define PRC_RVA22 {PRC_RVA, {1.8, 1.5, 1.5, FLT_LP, 1000, 1000, 0}, PFNZERO } // water S
3980#define PRC_RVA23 {PRC_RVA, {1.9, 1.75, 1.5, FLT_LP, 1000, 1000, 0}, PFNZERO } // water M
3981#define PRC_RVA24 {PRC_RVA, {2.0, 2.0, 1.5, FLT_LP, 1000, 1000, 0}, PFNZERO } // water L
3982
3983
3984/////////////
3985// Diffusors
3986/////////////
3987
3988// size: 0-1.0 scales all delays
3989// density: 0-1.0 controls # of series delays
3990// decay: 0-1.0 scales all feedback parameters
3991
3992// prctype size density decay
3993#define PRC_DFR1 {PRC_DFR, { 1.0, 0.5, 1.0 }, PFNZERO }
3994#define PRC_DFR2 {PRC_DFR, { 0.5, 0.3, 0.5 }, PFNZERO } // S
3995#define PRC_DFR3 {PRC_DFR, { 0.75, 0.5, 0.75 }, PFNZERO } // M
3996#define PRC_DFR4 {PRC_DFR, { 1.0, 0.5, 1.0 }, PFNZERO } // L
3997#define PRC_DFR5 {PRC_DFR, { 1.0, 1.0, 1.0 }, PFNZERO } // VL
3998
3999////////
4000// LFOs
4001////////
4002
4003// wavtype: lfo type to use (LFO_SIN, LFO_RND...)
4004// rate: modulation rate in hz. for MDY, 1/rate = 'glide' time in seconds
4005// foneshot: 1.0 if lfo is oneshot
4006
4007// prctype wavtype rate foneshot
4008#define PRC_LFO1 {PRC_LFO, { LFO_SIN, 440.0, 0.0, }, PFNZERO}
4009#define PRC_LFO2 {PRC_LFO, { LFO_SIN, 3000.0, 0.0, }, PFNZERO} // ear noise ring
4010#define PRC_LFO3 {PRC_LFO, { LFO_SIN, 4500.0, 0.0, }, PFNZERO} // ear noise ring
4011#define PRC_LFO4 {PRC_LFO, { LFO_SIN, 6000.0, 0.0, }, PFNZERO} // ear noise ring
4012#define PRC_LFO5 {PRC_LFO, { LFO_SAW, 100.0, 0.0, }, PFNZERO} // sub bass
4013
4014/////////
4015// Pitch
4016/////////
4017
4018// pitch: 0-n.0 where 1.0 = 1 octave up and 0.5 is one octave down
4019// timeslice: in milliseconds - size of sound chunk to analyze and cut/duplicate - 100ms nominal
4020// xfade: in milliseconds - size of crossfade region between spliced chunks - 20ms nominal
4021
4022// prctype pitch timeslice xfade
4023#define PRC_PTC1 {PRC_PTC, { 1.1, 100.0, 20.0 }, PFNZERO} // pitch up 10%
4024#define PRC_PTC2 {PRC_PTC, { 0.9, 100.0, 20.0 }, PFNZERO} // pitch down 10%
4025#define PRC_PTC3 {PRC_PTC, { 0.95, 100.0, 20.0 }, PFNZERO} // pitch down 5%
4026#define PRC_PTC4 {PRC_PTC, { 1.01, 100.0, 20.0 }, PFNZERO} // pitch up 1%
4027#define PRC_PTC5 {PRC_PTC, { 0.5, 100.0, 20.0 }, PFNZERO} // pitch down 50%
4028
4029/////////////
4030// Envelopes
4031/////////////
4032
4033// etype: ENV_LINEAR, ENV_LOG - currently ignored
4034// amp1: attack peak amplitude 0-1.0
4035// amp2: decay target amplitued 0-1.0
4036// amp3: sustain target amplitude 0-1.0
4037// attack time in milliseconds
4038// envelope decay time in milliseconds
4039// sustain time in milliseconds
4040// release time in milliseconds
4041
4042// prctype etype amp1 amp2 amp3 attack decay sustain release
4043#define PRC_ENV1 {PRC_ENV, {ENV_LIN, 1.0, 0.5, 0.4, 500, 500, 3000, 6000 }, PFNZERO}
4044
4045
4046//////////////
4047// Mod delays
4048//////////////
4049
4050// dtype: delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS
4051// delay: delay in milliseconds
4052// feedback: feedback 0-1.0
4053// gain: final gain of output stage, 0-1.0
4054
4055// modrate: frequency at which delay values change to new random value. 0 is no self-modulation
4056// moddepth: how much delay changes (decreases) from current value (0-1.0)
4057// modglide: glide time between dcur and dnew in milliseconds
4058
4059// prctype dtype delay feedback gain ftype cutoff qwidth qual modrate moddepth modglide
4060#define PRC_MDY1 {PRC_MDY, {DLY_PLAIN, 500.0, 0.5, 1.0, 0, 0, 0, 0, 10, 0.8, 5,}, PFNZERO}
4061#define PRC_MDY2 {PRC_MDY, {DLY_PLAIN, 50.0, 0.8, 1.0, 0, 0, 0, 0, 5, 0.8, 5,}, PFNZERO}
4062
4063#define PRC_MDY3 {PRC_MDY, {DLY_PLAIN, 300.0, 0.2, 1.0, 0, 0, 0, 0, 30, 0.01, 15,}, PFNZERO } // weird 1
4064#define PRC_MDY4 {PRC_MDY, {DLY_PLAIN, 400.0, 0.3, 1.0, 0, 0, 0, 0, 0.25, 0.01, 15,}, PFNZERO } // weird 2
4065#define PRC_MDY5 {PRC_MDY, {DLY_PLAIN, 500.0, 0.4, 1.0, 0, 0, 0, 0, 0.25, 0.01, 15,}, PFNZERO } // weird 3
4066
4067//////////
4068// Chorus
4069//////////
4070
4071// lfowav: lfotype is LFO_SIN, LFO_RND, LFO_TRI etc (LFO_RND for chorus, LFO_SIN for flange)
4072// rate: rate is modulation frequency in Hz
4073// depth: depth is modulation depth, 0-1.0
4074// mix: mix is mix of chorus and clean signal
4075
4076// prctype lfowav rate depth mix
4077#define PRC_CRS1 {PRC_CRS, { LFO_SIN, 10, 1.0, 0.5, }, PFNZERO }
4078
4079/////////////////////
4080// Envelope follower
4081/////////////////////
4082
4083// takes no parameters
4084#define PRC_EFO1 {PRC_EFO, { PRMZERO }, PFNZERO }
4085
4086// init array of processors - first store pfnParam, pfnGetNext and pfnFree functions for type,
4087// then call the pfnParam function to initialize each processor
4088
4089// prcs - an array of prc structures, all with initialized params
4090// count - number of elements in the array
4091// returns false if failed to init one or more processors
4092
4093qboolean PRC_InitAll( prc_t *prcs, int count )
4094{
4095 int i;
4096 prc_Param_t pfnParam; // allocation function - takes ptr to prc, returns ptr to specialized data struct for proc type
4097 prc_GetNext_t pfnGetNext; // get next function
4098 prc_GetNextN_t pfnGetNextN; // get next function, batch version
4099 prc_Free_t pfnFree;
4100 prc_Mod_t pfnMod;
4101 qboolean fok = true;
4102
4103 // set up pointers to XXX_Free, XXX_GetNext and XXX_Params functions
4104
4105 for( i = 0; i < count; i++ )
4106 {
4107 switch (prcs[i].type)
4108 {
4109 case PRC_DLY:
4110 pfnFree = (prc_Free_t)&DLY_Free;
4111 pfnGetNext = (prc_GetNext_t)&DLY_GetNext;
4112 pfnGetNextN = (prc_GetNextN_t)&DLY_GetNextN;
4113 pfnParam = &DLY_VParams;
4114 pfnMod = (prc_Mod_t)&DLY_Mod;
4115 break;
4116 case PRC_RVA:
4117 pfnFree = (prc_Free_t)&RVA_Free;
4118 pfnGetNext = (prc_GetNext_t)&RVA_GetNext;
4119 pfnGetNextN = (prc_GetNextN_t)&RVA_GetNextN;
4120 pfnParam = &RVA_VParams;
4121 pfnMod = (prc_Mod_t)&RVA_Mod;
4122 break;
4123 case PRC_FLT:
4124 pfnFree = (prc_Free_t)&FLT_Free;
4125 pfnGetNext = (prc_GetNext_t)&FLT_GetNext;
4126 pfnGetNextN = (prc_GetNextN_t)&FLT_GetNextN;
4127 pfnParam = &FLT_VParams;
4128 pfnMod = (prc_Mod_t)&FLT_Mod;
4129 break;
4130 case PRC_CRS:
4131 pfnFree = (prc_Free_t)&CRS_Free;
4132 pfnGetNext = (prc_GetNext_t)&CRS_GetNext;
4133 pfnGetNextN = (prc_GetNextN_t)&CRS_GetNextN;
4134 pfnParam = &CRS_VParams;
4135 pfnMod = (prc_Mod_t)&CRS_Mod;
4136 break;
4137 case PRC_PTC:
4138 pfnFree = (prc_Free_t)&PTC_Free;
4139 pfnGetNext = (prc_GetNext_t)&PTC_GetNext;
4140 pfnGetNextN = (prc_GetNextN_t)&PTC_GetNextN;
4141 pfnParam = &PTC_VParams;
4142 pfnMod = (prc_Mod_t)&PTC_Mod;
4143 break;
4144 case PRC_ENV:
4145 pfnFree = (prc_Free_t)&ENV_Free;
4146 pfnGetNext = (prc_GetNext_t)&ENV_GetNext;
4147 pfnGetNextN = (prc_GetNextN_t)&ENV_GetNextN;
4148 pfnParam = &ENV_VParams;
4149 pfnMod = (prc_Mod_t)&ENV_Mod;
4150 break;
4151 case PRC_LFO:
4152 pfnFree = (prc_Free_t)&LFO_Free;
4153 pfnGetNext = (prc_GetNext_t)&LFO_GetNext;
4154 pfnGetNextN = (prc_GetNextN_t)&LFO_GetNextN;
4155 pfnParam = &LFO_VParams;
4156 pfnMod = (prc_Mod_t)&LFO_Mod;
4157 break;
4158 case PRC_EFO:
4159 pfnFree = (prc_Free_t)&EFO_Free;
4160 pfnGetNext = (prc_GetNext_t)&EFO_GetNext;
4161 pfnGetNextN = (prc_GetNextN_t)&EFO_GetNextN;
4162 pfnParam = &EFO_VParams;
4163 pfnMod = (prc_Mod_t)&EFO_Mod;
4164 break;
4165 case PRC_MDY:
4166 pfnFree = (prc_Free_t)&MDY_Free;
4167 pfnGetNext = (prc_GetNext_t)&MDY_GetNext;
4168 pfnGetNextN = (prc_GetNextN_t)&MDY_GetNextN;
4169 pfnParam = &MDY_VParams;
4170 pfnMod = (prc_Mod_t)&MDY_Mod;
4171 break;
4172 case PRC_DFR:
4173 pfnFree = (prc_Free_t)&DFR_Free;
4174 pfnGetNext = (prc_GetNext_t)&DFR_GetNext;
4175 pfnGetNextN = (prc_GetNextN_t)&DFR_GetNextN;
4176 pfnParam = &DFR_VParams;
4177 pfnMod = (prc_Mod_t)&DFR_Mod;
4178 break;
4179 case PRC_AMP:
4180 pfnFree = (prc_Free_t)&_Free;
4181 pfnGetNext = (prc_GetNext_t)&_GetNext;
4182 pfnGetNextN = (prc_GetNextN_t)&_GetNextN;
4183 pfnParam = &_VParams;
4184 pfnMod = (prc_Mod_t)&_Mod;
4185 break;
4186 case PRC_NULL:
4187 default:
4188 pfnFree = (prc_Free_t)&NULL_Free;
4189 pfnGetNext = (prc_GetNext_t)&NULL_GetNext;
4190 pfnGetNextN = (prc_GetNextN_t)&NULL_GetNextN;
4191 pfnParam = &NULL_VParams;
4192 pfnMod = (prc_Mod_t)&NULL_Mod;
4193 break;
4194 }
4195
4196 // set up function pointers
4197 prcs[i].pfnParam = pfnParam;
4198 prcs[i].pfnGetNext = pfnGetNext;
4199 prcs[i].pfnGetNextN = pfnGetNextN;
4200 prcs[i].pfnFree = pfnFree;
4201
4202 // call param function, store pdata for the processor type
4203 prcs[i].pdata = pfnParam((void *)( &prcs[i] ));
4204
4205 if( !prcs[i].pdata )
4206 fok = false;
4207 }
4208 return fok;
4209}
4210
4211// free individual processor's data
4212void PRC_Free( prc_t *pprc )
4213{
4214 if( pprc->pfnFree && pprc->pdata )
4215 pprc->pfnFree( pprc->pdata );
4216}
4217
4218// free all processors for supplied array
4219// prcs - array of processors
4220// count - elements in array
4221void PRC_FreeAll( prc_t *prcs, int count )
4222{
4223 int i;
4224
4225 for( i = 0; i < count; i++ )
4226 PRC_Free( &prcs[i] );
4227}
4228
4229// get next value for processor - (usually called directly by PSET_GetNext)
4230inline int PRC_GetNext( prc_t *pprc, int x )
4231{
4232 return pprc->pfnGetNext( pprc->pdata, x );
4233}
4234
4235// automatic parameter range limiting
4236// force parameters between specified min/max in param_rng
4237void PRC_CheckParams( prc_t *pprc, prm_rng_t *prng )
4238{
4239 // first entry in param_rng is # of parameters
4240 int cprm = prng[0].iprm;
4241 int i;
4242
4243 for( i = 0; i < cprm; i++)
4244 {
4245 // if parameter is 0.0f, always allow it (this is 'off' for most params)
4246 if( pprc->prm[i] != 0.0f && ( pprc->prm[i] > prng[i+1].hi || pprc->prm[i] < prng[i+1].lo ))
4247 {
4248 Con_DPrintf("DSP: clamping out of range parameter.\n");
4249 pprc->prm[i] = bound( prng[i+1].lo, pprc->prm[i], prng[i+1].hi );
4250 }
4251 }
4252}
4253
4254// DSP presets
4255// A dsp preset comprises one or more dsp processors in linear, parallel or feedback configuration
4256// preset configurations
4257//
4258#define PSET_SIMPLE 0
4259
4260// x(n)--->P(0)--->y(n)
4261#define PSET_LINEAR 1
4262
4263// x(n)--->P(0)-->P(1)-->...P(m)--->y(n)
4264#define PSET_PARALLEL6 4
4265
4266// x(n)-P(0)-->P(1)-->P(2)-->(+)-P(5)->y(n)
4267// | ^
4268// | |
4269// -->P(3)-->P(4)---->
4270
4271
4272#define PSET_PARALLEL2 5
4273
4274// x(n)--->P(0)-->(+)-->y(n)
4275// ^
4276// |
4277// x(n)--->P(1)-----
4278
4279#define PSET_PARALLEL4 6
4280
4281// x(n)--->P(0)-->P(1)-->(+)-->y(n)
4282// ^
4283// |
4284// x(n)--->P(2)-->P(3)-----
4285
4286#define PSET_PARALLEL5 7
4287
4288// x(n)--->P(0)-->P(1)-->(+)-->P(4)-->y(n)
4289// ^
4290// |
4291// x(n)--->P(2)-->P(3)-----
4292
4293#define PSET_FEEDBACK 8
4294
4295// x(n)-P(0)--(+)-->P(1)-->P(2)-->P(5)->y(n)
4296// ^ |
4297// | v
4298// -----P(4)<--P(3)--
4299
4300#define PSET_FEEDBACK3 9
4301
4302// x(n)---(+)-->P(0)--------->y(n)
4303// ^ |
4304// | v
4305// -----P(2)<--P(1)--
4306
4307#define PSET_FEEDBACK4 10
4308
4309// x(n)---(+)-->P(0)-------->P(3)--->y(n)
4310// ^ |
4311// | v
4312// ---P(2)<--P(1)--
4313
4314#define PSET_MOD 11
4315
4316//
4317// x(n)------>P(1)--P(2)--P(3)--->y(n)
4318// ^
4319// x(n)------>P(0)....:
4320
4321#define PSET_MOD2 12
4322
4323//
4324// x(n)-------P(1)-->y(n)
4325// ^
4326// x(n)-->P(0)..:
4327
4328
4329#define PSET_MOD3 13
4330
4331//
4332// x(n)-------P(1)-->P(2)-->y(n)
4333// ^
4334// x(n)-->P(0)..:
4335
4336
4337#define CPSETS 64 // max number of presets simultaneously active
4338
4339#define CPSET_PRCS 6 // max # of processors per dsp preset
4340#define CPSET_STATES (CPSET_PRCS+3) // # of internal states
4341
4342// NOTE: do not reorder members of pset_t - psettemplates relies on it!!!
4343typedef struct
4344{
4345 int type; // preset configuration type
4346 int cprcs; // number of processors for this preset
4347 prc_t prcs[CPSET_PRCS]; // processor preset data
4348 float gain; // preset gain 0.1->2.0
4349 int w[CPSET_STATES]; // internal states
4350 int fused;
4351} pset_t;
4352
4353pset_t psets[CPSETS];
4354
4355// array of dsp presets, each with up to 6 processors per preset
4356
4357#define WZERO {0,0,0,0,0,0,0,0,0}, 0
4358
4359pset_t psettemplates[] =
4360{
4361// presets 0-29 map to legacy room_type 0-29
4362
4363// type # proc P0 P1 P2 P3 P4 P5 GAIN
4364{PSET_SIMPLE, 1, { PRC_NULL1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // OFF 0
4365{PSET_SIMPLE, 1, { PRC_RVA18, PRC0, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // GENERIC 1 // general, low reflective, diffuse room
4366{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA3, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // METALIC_S 2 // highly reflective, parallel surfaces
4367{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA4, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // METALIC_M 3
4368{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA5, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // METALIC_L 4
4369{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA6, PRC0, PRC0, PRC0, PRC0 },2.0, WZERO }, // TUNNEL_S 5 // resonant reflective, long surfaces
4370{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA7, PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // TUNNEL_M 6
4371{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA8, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // TUNNEL_L 7
4372{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA12,PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // CHAMBER_S 8 // diffuse, moderately reflective surfaces
4373{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA13,PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // CHAMBER_M 9
4374{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA14,PRC0, PRC0, PRC0, PRC0 },1.9, WZERO }, // CHAMBER_L 10
4375{PSET_SIMPLE, 1, { PRC_RVA15, PRC0, PRC0, PRC0, PRC0, PRC0 },1.5, WZERO }, // BRITE_S 11 // diffuse, highly reflective
4376{PSET_SIMPLE, 1, { PRC_RVA16, PRC0, PRC0, PRC0, PRC0, PRC0 },1.6, WZERO }, // BRITE_M 12
4377{PSET_SIMPLE, 1, { PRC_RVA17, PRC0, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // BRITE_L 13
4378{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA22,PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WATER1 14 // underwater fx
4379{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA23,PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WATER2 15
4380{PSET_LINEAR, 3, { PRC_DFR1, PRC_RVA24,PRC_MDY5, PRC0, PRC0, PRC0 },1.8, WZERO }, // WATER3 16
4381{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA19,PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // CONCRTE_S 17 // bare, reflective, parallel surfaces
4382{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA20,PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // CONCRTE_M 18
4383{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA21,PRC0, PRC0, PRC0, PRC0 },1.9, WZERO }, // CONCRTE_L 19
4384{PSET_LINEAR, 2, { PRC_DFR1, PRC_DLY3, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // OUTSIDE1 20 // echoing, moderately reflective
4385{PSET_LINEAR, 2, { PRC_DFR1, PRC_DLY4, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // OUTSIDE2 21 // echoing, dull
4386{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_DLY5, PRC0, PRC0, PRC0 },1.6, WZERO }, // OUTSIDE3 22 // echoing, very dull
4387{PSET_LINEAR, 2, { PRC_DLY10, PRC_RVA10,PRC0, PRC0, PRC0, PRC0 },2.8, WZERO }, // CAVERN_S 23 // large, echoing area
4388{PSET_LINEAR, 2, { PRC_DLY11, PRC_RVA10,PRC0, PRC0, PRC0, PRC0 },2.6, WZERO }, // CAVERN_M 24
4389{PSET_LINEAR, 3, { PRC_DFR1, PRC_DLY12,PRC_RVA11,PRC0, PRC0, PRC0 },2.6, WZERO }, // CAVERN_L 25
4390{PSET_LINEAR, 2, { PRC_DLY7, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },2.0, WZERO }, // WEIRDO1 26
4391{PSET_LINEAR, 2, { PRC_DLY8, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },1.9, WZERO }, // WEIRDO2 27
4392{PSET_LINEAR, 2, { PRC_DLY9, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WEIRDO3 28
4393{PSET_LINEAR, 2, { PRC_DLY9, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WEIRDO4 29
4394
4395// presets 30-40 are new presets
4396{PSET_SIMPLE, 1, { PRC_FLT2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 30 lowpass - facing away
4397{PSET_LINEAR, 2, { PRC_FLT3, PRC_DLY14,PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 31 lowpass - facing away+80ms delay
4398//{PSET_PARALLEL2,2, { PRC_AMP6, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 32 explosion ring 1
4399//{PSET_PARALLEL2,2, { PRC_AMP7, PRC_LFO3, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 33 explosion ring 2
4400//{PSET_PARALLEL2,2, { PRC_AMP8, PRC_LFO4, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 34 explosion ring 3
4401{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_FLT3, PRC0, PRC0, PRC0 },0.25, WZERO }, // 32 explosion ring
4402{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_FLT3, PRC0, PRC0, PRC0 },0.25, WZERO }, // 33 explosion ring 2
4403{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_FLT3, PRC0, PRC0, PRC0 },0.25, WZERO }, // 34 explosion ring 3
4404{PSET_PARALLEL2,2, { PRC_DFR1, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },0.25, WZERO }, // 35 shock muffle 1
4405{PSET_PARALLEL2,2, { PRC_DFR1, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },0.25, WZERO }, // 36 shock muffle 2
4406{PSET_PARALLEL2,2, { PRC_DFR1, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },0.25, WZERO }, // 37 shock muffle 3
4407//{PSET_LINEAR, 3, { PRC_DFR1, PRC_LFO4, PRC_FLT3, PRC0, PRC0, PRC0 },1.0, WZERO }, // 35 shock muffle 1
4408//{PSET_LINEAR, 3, { PRC_DFR1, PRC_LFO4, PRC_FLT3, PRC0, PRC0, PRC0 },1.0, WZERO }, // 36 shock muffle 2
4409//{PSET_LINEAR, 3, { PRC_DFR1, PRC_LFO4, PRC_FLT3, PRC0, PRC0, PRC0 },1.0, WZERO }, // 37 shock muffle 3
4410{PSET_FEEDBACK3,3, { PRC_DLY13, PRC_PTC4, PRC_FLT2, PRC0, PRC0, PRC0 },0.25, WZERO }, // 38 fade pitchdown 1
4411{PSET_LINEAR, 3, { PRC_AMP3, PRC_FLT5, PRC_FLT6, PRC0, PRC0, PRC0 },2.0, WZERO }, // 39 distorted speaker 1
4412
4413// fade out fade in
4414
4415// presets 40+ are test presets
4416{PSET_SIMPLE, 1, { PRC_NULL1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 39 null
4417{PSET_SIMPLE, 1, { PRC_DLY1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 40 delay
4418{PSET_SIMPLE, 1, { PRC_RVA1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 41 parallel reverb
4419{PSET_SIMPLE, 1, { PRC_DFR1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 42 series diffusor
4420{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA1, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 43 diff & reverb
4421{PSET_SIMPLE, 1, { PRC_DLY2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 44 lowpass delay
4422{PSET_SIMPLE, 1, { PRC_MDY2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 45 modulating delay
4423{PSET_SIMPLE, 1, { PRC_PTC1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 46 pitch shift
4424{PSET_SIMPLE, 1, { PRC_PTC2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 47 pitch shift
4425{PSET_SIMPLE, 1, { PRC_FLT1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 48 filter
4426{PSET_SIMPLE, 1, { PRC_CRS1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 49 chorus
4427{PSET_SIMPLE, 1, { PRC_ENV1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 50
4428{PSET_SIMPLE, 1, { PRC_LFO1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 51 lfo
4429{PSET_SIMPLE, 1, { PRC_EFO1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 52
4430{PSET_SIMPLE, 1, { PRC_MDY1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 53 modulating delay
4431{PSET_SIMPLE, 1, { PRC_FLT2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 54 lowpass - facing away
4432{PSET_PARALLEL2, 2, { PRC_PTC2, PRC_PTC1, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 55 ptc1/ptc2
4433{PSET_FEEDBACK, 6, { PRC_DLY1, PRC0, PRC0, PRC_PTC1, PRC_FLT1, PRC0 },1.0, WZERO }, // 56 dly/ptc1
4434{PSET_MOD, 4, { PRC_EFO1, PRC0, PRC_PTC1, PRC0, PRC0, PRC0 },1.0, WZERO }, // 57 efo mod ptc
4435{PSET_LINEAR, 3, { PRC_DLY1, PRC_RVA1, PRC_CRS1, PRC0, PRC0, PRC0 },1.0, WZERO } // 58 dly/rvb/crs
4436};
4437
4438
4439// number of presets currently defined above
4440
4441#define CPSETTEMPLATES 60 //(sizeof( psets ) / sizeof( pset_t ))
4442
4443// init a preset - just clear state array
4444void PSET_Init( pset_t *ppset )
4445{
4446 // clear state array
4447 if( ppset ) Q_memset( ppset->w, 0, sizeof( int ) * ( CPSET_STATES ));
4448}
4449
4450// clear runtime slots
4451void PSET_InitAll( void )
4452{
4453 int i;
4454
4455 for( i = 0; i < CPSETS; i++ )
4456 Q_memset( &psets[i], 0, sizeof( pset_t ));
4457}
4458
4459// free the preset - free all processors
4460
4461void PSET_Free( pset_t *ppset )
4462{
4463 if( ppset )
4464 {
4465 // free processors
4466 PRC_FreeAll( ppset->prcs, ppset->cprcs );
4467
4468 // clear
4469 Q_memset( ppset, 0, sizeof( pset_t ));
4470 }
4471}
4472
4473void PSET_FreeAll() { int i; for( i = 0; i < CPSETS; i++ ) PSET_Free( &psets[i] ); };
4474
4475// return preset struct, given index into preset template array
4476// NOTE: should not ever be more than 2 or 3 of these active simultaneously
4477pset_t *PSET_Alloc( int ipsettemplate )
4478{
4479 pset_t *ppset;
4480 qboolean fok;
4481 int i;
4482
4483 // don't excede array bounds
4484 if( ipsettemplate >= CPSETTEMPLATES )
4485 ipsettemplate = 0;
4486
4487 // find free slot
4488 for( i = 0; i < CPSETS; i++)
4489 {
4490 if( !psets[i].fused )
4491 break;
4492 }
4493
4494 if( i == CPSETS )
4495 return NULL;
4496
4497 ppset = &psets[i];
4498
4499 // copy template into preset
4500 *ppset = psettemplates[ipsettemplate];
4501
4502 ppset->fused = true;
4503
4504 // clear state array
4505 PSET_Init( ppset );
4506
4507 // init all processors, set up processor function pointers
4508 fok = PRC_InitAll( ppset->prcs, ppset->cprcs );
4509
4510 if( !fok )
4511 {
4512 // failed to init one or more processors
4513 Sys_Error ("Sound DSP: preset failed to init.");
4514 PRC_FreeAll( ppset->prcs, ppset->cprcs );
4515 return NULL;
4516 }
4517 return ppset;
4518}
4519
4520// batch version of PSET_GetNext for linear array of processors. For performance.
4521
4522// ppset - preset array
4523// pbuffer - input sample data
4524// SampleCount - size of input buffer
4525// OP: OP_LEFT - process left channel in place
4526// OP_RIGHT - process right channel in place
4527// OP_LEFT_DUPLICATe - process left channel, duplicate into right
4528
4529inline void PSET_GetNextN( pset_t *ppset, portable_samplepair_t *pbf, int SampleCount, int op )
4530{
4531 prc_t *pprc;
4532 int i, count = ppset->cprcs;
4533
4534 switch( ppset->type )
4535 {
4536 default:
4537 case PSET_SIMPLE:
4538 {
4539 // x(n)--->P(0)--->y(n)
4540 ppset->prcs[0].pfnGetNextN( ppset->prcs[0].pdata, pbf, SampleCount, op );
4541 break;
4542 }
4543 case PSET_LINEAR:
4544 {
4545
4546 // w0 w1 w2
4547 // x(n)--->P(0)-->P(1)-->...P(count-1)--->y(n)
4548
4549 // w0 w1 w2 w3 w4 w5
4550 // x(n)--->P(0)-->P(1)-->P(2)-->P(3)-->P(4)-->y(n)
4551
4552 // call batch processors in sequence - no internal state for batch processing
4553 // point to first processor
4554
4555 pprc = &ppset->prcs[0];
4556
4557 for( i = 0; i < count; i++ )
4558 {
4559 pprc->pfnGetNextN( pprc->pdata, pbf, SampleCount, op );
4560 pprc++;
4561 }
4562 break;
4563 }
4564 }
4565}
4566
4567
4568// Get next sample from this preset. called once for every sample in buffer
4569// ppset is pointer to preset
4570// x is input sample
4571inline int PSET_GetNext( pset_t *ppset, int x )
4572{
4573 int *w = ppset->w;
4574 prc_t *pprc;
4575 int count = ppset->cprcs;
4576
4577 // initialized 0'th element of state array
4578
4579 w[0] = x;
4580
4581 switch( ppset->type )
4582 {
4583 default:
4584 case PSET_SIMPLE:
4585 {
4586 // x(n)--->P(0)--->y(n)
4587 return ppset->prcs[0].pfnGetNext (ppset->prcs[0].pdata, x);
4588 }
4589 case PSET_LINEAR:
4590 {
4591 // w0 w1 w2
4592 // x(n)--->P(0)-->P(1)-->...P(count-1)--->y(n)
4593
4594 // w0 w1 w2 w3 w4 w5
4595 // x(n)--->P(0)-->P(1)-->P(2)-->P(3)-->P(4)-->y(n)
4596
4597 // call processors in reverse order, from count to 1
4598
4599 // point to last processor
4600
4601 pprc = &ppset->prcs[count-1];
4602
4603 switch( count )
4604 {
4605 default:
4606 case 5:
4607 w[5] = pprc->pfnGetNext (pprc->pdata, w[4]);
4608 pprc--;
4609 case 4:
4610 w[4] = pprc->pfnGetNext (pprc->pdata, w[3]);
4611 pprc--;
4612 case 3:
4613 w[3] = pprc->pfnGetNext (pprc->pdata, w[2]);
4614 pprc--;
4615 case 2:
4616 w[2] = pprc->pfnGetNext (pprc->pdata, w[1]);
4617 pprc--;
4618 case 1:
4619 w[1] = pprc->pfnGetNext (pprc->pdata, w[0]);
4620 }
4621 return w[count];
4622 }
4623
4624 case PSET_PARALLEL6:
4625 {
4626 // w0 w1 w2 w3 w6 w7
4627 // x(n)-P(0)-->P(1)-->P(2)-->(+)---P(5)--->y(n)
4628 // | ^
4629 // | w4 w5 |
4630 // -->P(3)-->P(4)---->
4631
4632 pprc = &ppset->prcs[0];
4633
4634 // start with all adders
4635
4636 w[6] = w[3] + w[5];
4637
4638 // top branch - evaluate in reverse order
4639
4640 w[7] = pprc[5].pfnGetNext( pprc[5].pdata, w[6] );
4641 w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[2] );
4642 w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[1] );
4643
4644 // bottom branch - evaluate in reverse order
4645
4646 w[5] = pprc[4].pfnGetNext( pprc[4].pdata, w[4] );
4647 w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[1] );
4648
4649 w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
4650
4651 return w[7];
4652 }
4653 case PSET_PARALLEL2:
4654 { // w0 w1 w3
4655 // x(n)--->P(0)-->(+)-->y(n)
4656 // ^
4657 // w0 w2 |
4658 // x(n)--->P(1)-----
4659
4660 pprc = &ppset->prcs[0];
4661
4662 w[3] = w[1] + w[2];
4663
4664 w[1] = pprc->pfnGetNext( pprc->pdata, w[0] );
4665 pprc++;
4666 w[2] = pprc->pfnGetNext( pprc->pdata, w[0] );
4667
4668 return w[3];
4669 }
4670
4671 case PSET_PARALLEL4:
4672 {
4673 // w0 w1 w2 w5
4674 // x(n)--->P(0)-->P(1)-->(+)-->y(n)
4675 // ^
4676 // w0 w3 w4 |
4677 // x(n)--->P(2)-->P(3)-----
4678
4679 pprc = &ppset->prcs[0];
4680
4681 w[5] = w[2] + w[4];
4682
4683 w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[1] );
4684 w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[3] );
4685
4686 w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
4687 w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[0] );
4688
4689 return w[5];
4690 }
4691
4692 case PSET_PARALLEL5:
4693 {
4694 // w0 w1 w2 w5 w6
4695 // x(n)--->P(0)-->P(1)-->(+)-->P(4)-->y(n)
4696 // ^
4697 // w0 w3 w4 |
4698 // x(n)--->P(2)-->P(3)-----
4699
4700 pprc = &ppset->prcs[0];
4701
4702 w[5] = w[2] + w[4];
4703
4704 w[6] = pprc[4].pfnGetNext( pprc[4].pdata, w[5] );
4705
4706 w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[1] );
4707 w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[3] );
4708
4709 w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
4710 w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[0] );
4711
4712 return w[6];
4713 }
4714
4715 case PSET_FEEDBACK:
4716 {
4717 // w0 w1 w2 w3 w4 w7
4718 // x(n)-P(0)--(+)-->P(1)-->P(2)-->P(5)->y(n)
4719 // ^ |
4720 // | w6 w5 v
4721 // -----P(4)<--P(3)--
4722
4723 pprc = &ppset->prcs[0];
4724
4725 // start with adders
4726
4727 w[2] = w[1] + w[6];
4728
4729 // evaluate in reverse order
4730
4731 w[7] = pprc[5].pfnGetNext( pprc[5].pdata, w[4] );
4732 w[6] = pprc[4].pfnGetNext( pprc[4].pdata, w[5] );
4733 w[5] = pprc[3].pfnGetNext( pprc[3].pdata, w[4] );
4734 w[4] = pprc[2].pfnGetNext( pprc[2].pdata, w[3] );
4735 w[3] = pprc[1].pfnGetNext( pprc[1].pdata, w[2] );
4736 w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
4737
4738 return w[7];
4739 }
4740 case PSET_FEEDBACK3:
4741 {
4742 // w0 w1 w2
4743 // x(n)---(+)-->P(0)--------->y(n)
4744 // ^ |
4745 // | w4 w3 v
4746 // -----P(2)<--P(1)--
4747
4748 pprc = &ppset->prcs[0];
4749
4750 // start with adders
4751
4752 w[1] = w[0] + w[4];
4753
4754 // evaluate in reverse order
4755
4756 w[4] = pprc[2].pfnGetNext( pprc[2].pdata, w[3] );
4757 w[3] = pprc[1].pfnGetNext( pprc[1].pdata, w[2] );
4758 w[2] = pprc[0].pfnGetNext( pprc[0].pdata, w[1] );
4759
4760 return w[2];
4761 }
4762 case PSET_FEEDBACK4:
4763 {
4764 // w0 w1 w2 w5
4765 // x(n)---(+)-->P(0)-------->P(3)--->y(n)
4766 // ^ |
4767 // | w4 w3 v
4768 // ---P(2)<--P(1)--
4769
4770 pprc = &ppset->prcs[0];
4771
4772 // start with adders
4773
4774 w[1] = w[0] + w[4];
4775
4776 // evaluate in reverse order
4777
4778 w[5] = pprc[3].pfnGetNext( pprc[3].pdata, w[2] );
4779 w[4] = pprc[2].pfnGetNext( pprc[2].pdata, w[3] );
4780 w[3] = pprc[1].pfnGetNext( pprc[1].pdata, w[2] );
4781 w[2] = pprc[0].pfnGetNext( pprc[0].pdata, w[1] );
4782
4783 return w[2];
4784 }
4785 case PSET_MOD:
4786 {
4787 // w0 w1 w3 w4
4788 // x(n)------>P(1)--P(2)--P(3)--->y(n)
4789 // w0 w2 ^
4790 // x(n)------>P(0)....:
4791
4792 pprc = &ppset->prcs[0];
4793
4794 w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[3] );
4795
4796 w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[1] );
4797
4798 // modulate processor 2
4799
4800 pprc[2].pfnMod( pprc[2].pdata, ((float)w[2] / (float)PMAX));
4801
4802 // get modulator output
4803
4804 w[2] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
4805
4806 w[1] = pprc[1].pfnGetNext( pprc[1].pdata, w[0] );
4807
4808 return w[4];
4809 }
4810 case PSET_MOD2:
4811 {
4812 // w0 w2
4813 // x(n)---------P(1)-->y(n)
4814 // w0 w1 ^
4815 // x(n)-->P(0)....:
4816
4817 pprc = &ppset->prcs[0];
4818
4819 // modulate processor 1
4820
4821 pprc[1].pfnMod( pprc[1].pdata, ((float)w[1] / (float)PMAX));
4822
4823 // get modulator output
4824
4825 w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
4826
4827 w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[0] );
4828
4829 return w[2];
4830
4831 }
4832 case PSET_MOD3:
4833 {
4834 // w0 w2 w3
4835 // x(n)----------P(1)-->P(2)-->y(n)
4836 // w0 w1 ^
4837 // x(n)-->P(0).....:
4838
4839 pprc = &ppset->prcs[0];
4840
4841 w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[2] );
4842
4843 // modulate processor 1
4844
4845 pprc[1].pfnMod( pprc[1].pdata, ((float)w[1] / (float)PMAX));
4846
4847 // get modulator output
4848
4849 w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
4850
4851 w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[0] );
4852
4853 return w[2];
4854 }
4855 }
4856}
4857
4858
4859/////////////
4860// DSP system
4861/////////////
4862
4863// Main interface
4864
4865// Whenever the preset # changes on any of these processors, the old processor is faded out, new is faded in.
4866// dsp_chan is optionally set when a sound is played - a preset is sent with the start_static/dynamic sound.
4867//
4868// sound1---->dsp_chan--> -------------(+)---->dsp_water--->dsp_player--->out
4869// sound2---->dsp_chan--> | |
4870// sound3---------------> ----dsp_room---
4871// | |
4872// --dsp_indirect-
4873
4874// dsp_room - set this cvar to a preset # to change the room dsp. room fx are more prevalent farther from player.
4875// use: when player moves into a new room, all sounds played in room take on its reverberant character
4876// dsp_water - set this cvar (once) to a preset # for serial underwater sound.
4877// use: when player goes under water, all sounds pass through this dsp (such as low pass filter)
4878// dsp_player - set this cvar to a preset # to cause all sounds to run through the effect (serial, in-line).
4879// use: player is deafened, player fires special weapon, player is hit by special weapon.
4880// dsp_facingaway- set this cvar to a preset # appropriate for sounds which are played facing away from player (weapon,voice)
4881
4882// Dsp presets
4883
4884//convar_t *dsp_room; // room dsp preset - sounds more distant from player (1ch)
4885cvar_t dsp_room = {"dsp_room","0"}; // // room dsp preset - sounds more distant from player (1ch)
4886
4887int ipset_room_prev;
4888
4889// legacy room_type support
4890cvar_t dsp_off = {"dsp_off","0"};
4891cvar_t dsp_room_type = {"dsp_room_type","0"};
4892int ipset_room_typeprev;
4893
4894
4895// DSP processors
4896
4897int idsp_room;
4898//convar_t *dsp_stereo; // set to 1 for true stereo processing. 2x perf hit.
4899cvar_t dsp_stereo = {"dsp_stereo","1"}; // set to 1 for true stereo processing. 2x perf hit.
4900
4901// DSP preset executor
4902#define CDSPS 32 // max number dsp executors active
4903#define DSPCHANMAX 4 // max number of channels dsp can process (allocs a separte processor for each chan)
4904
4905typedef struct
4906{
4907 qboolean fused;
4908 int cchan; // 1-4 channels, ie: mono, FrontLeft, FrontRight, RearLeft, RearRight
4909 pset_t *ppset[DSPCHANMAX]; // current preset (1-4 channels)
4910 int ipset; // current ipreset
4911 pset_t *ppsetprev[DSPCHANMAX]; // previous preset (1-4 channels)
4912 int ipsetprev; // previous ipreset
4913 float xfade; // crossfade time between previous preset and new
4914 rmp_t xramp; // crossfade ramp
4915} dsp_t;
4916
4917dsp_t dsps[CDSPS];
4918
4919void DSP_Init( int idsp )
4920{
4921 dsp_t *pdsp;
4922
4923 if( idsp < 0 || idsp > CDSPS )
4924 return;
4925
4926 pdsp = &dsps[idsp];
4927 Q_memset( pdsp, 0, sizeof( dsp_t ));
4928}
4929
4930void DSP_Free( int idsp )
4931{
4932 dsp_t *pdsp;
4933 int i;
4934
4935 if( idsp < 0 || idsp > CDSPS )
4936 return;
4937
4938 pdsp = &dsps[idsp];
4939
4940 for( i = 0; i < pdsp->cchan; i++ )
4941 {
4942 if( pdsp->ppset[i] )
4943 PSET_Free( pdsp->ppset[i] );
4944
4945 if( pdsp->ppsetprev[i] )
4946 PSET_Free( pdsp->ppsetprev[i] );
4947 }
4948
4949 Q_memset( pdsp, 0, sizeof( dsp_t ));
4950}
4951
4952// Init all dsp processors - called once, during engine startup
4953void DSP_InitAll( void )
4954{
4955 int idsp;
4956
4957 // order is important, don't rearange.
4958 FLT_InitAll();
4959 DLY_InitAll();
4960 RVA_InitAll();
4961 LFOWAV_InitAll();
4962 LFO_InitAll();
4963
4964 CRS_InitAll();
4965 PTC_InitAll();
4966 ENV_InitAll();
4967 EFO_InitAll();
4968 MDY_InitAll();
4969 AMP_InitAll();
4970
4971 PSET_InitAll();
4972
4973 for( idsp = 0; idsp < CDSPS; idsp++ )
4974 DSP_Init( idsp );
4975
4976 Con_Printf("Sound DSP Initialized\n");
4977}
4978
4979// free all resources associated with dsp - called once, during engine shutdown
4980
4981void DSP_FreeAll( void )
4982{
4983 int idsp;
4984
4985 // order is important, don't rearange.
4986 for( idsp = 0; idsp < CDSPS; idsp++ )
4987 DSP_Free( idsp );
4988
4989 AMP_FreeAll();
4990 MDY_FreeAll();
4991 EFO_FreeAll();
4992 ENV_FreeAll();
4993 PTC_FreeAll();
4994 CRS_FreeAll();
4995
4996 LFO_FreeAll();
4997 LFOWAV_FreeAll();
4998 RVA_FreeAll();
4999 DLY_FreeAll();
5000 FLT_FreeAll();
5001}
5002
5003
5004// allocate a new dsp processor chain, kill the old processor. Called by DSP_CheckNewPreset()
5005// ipset is new preset
5006// xfade is crossfade time when switching between presets (milliseconds)
5007// cchan is how many simultaneous preset channels to allocate (1-4)
5008// return index to new dsp
5009int DSP_Alloc( int ipset, float xfade, int cchan )
5010{
5011 dsp_t *pdsp;
5012 int i, idsp;
5013 int cchans = bound( 1, cchan, DSPCHANMAX);
5014
5015 // find free slot
5016 for( idsp = 0; idsp < CDSPS; idsp++ )
5017 {
5018 if( !dsps[idsp].fused )
5019 break;
5020 }
5021
5022 if( idsp == CDSPS )
5023 return -1;
5024
5025 pdsp = &dsps[idsp];
5026
5027 DSP_Init( idsp );
5028
5029 pdsp->fused = true;
5030 pdsp->cchan = cchans;
5031
5032 // allocate a preset processor for each channel
5033 pdsp->ipset = ipset;
5034 pdsp->ipsetprev = 0;
5035
5036 for( i = 0; i < pdsp->cchan; i++ )
5037 {
5038 pdsp->ppset[i] = PSET_Alloc( ipset );
5039 pdsp->ppsetprev[i] = NULL;
5040 }
5041
5042 // set up crossfade time in seconds
5043 pdsp->xfade = xfade / 1000.0f;
5044
5045 RMP_SetEnd( &pdsp->xramp );
5046
5047 return idsp;
5048}
5049
5050// return gain for current preset associated with dsp
5051// get crossfade to new gain if switching from previous preset (from preset crossfader value)
5052// Returns 1.0 gain if no preset (preset 0)
5053float DSP_GetGain( int idsp )
5054{
5055 float gain_target = 0.0;
5056 float gain_prev = 0.0;
5057 float gain;
5058 dsp_t *pdsp;
5059 int r;
5060
5061 if( idsp < 0 || idsp > CDSPS )
5062 return 1.0f;
5063
5064 pdsp = &dsps[idsp];
5065
5066 // get current preset's gain
5067 if( pdsp->ppset[0] )
5068 gain_target = pdsp->ppset[0]->gain;
5069 else gain_target = 1.0f;
5070
5071 // if not crossfading, return current preset gain
5072 if( RMP_HitEnd( &pdsp->xramp ))
5073 {
5074 // return current preset's gain
5075 return gain_target;
5076 }
5077
5078 // get previous preset gain
5079
5080 if( pdsp->ppsetprev[0] )
5081 gain_prev = pdsp->ppsetprev[0]->gain;
5082 else gain_prev = 1.0;
5083
5084 // if current gain = target preset gain, return
5085 if( gain_target == gain_prev )
5086 {
5087 if( gain_target == 0.0f )
5088 return 1.0f;
5089 return gain_target;
5090 }
5091
5092 // get crossfade ramp value (updated elsewhere, when actually crossfading preset data)
5093 r = RMP_GetCurrent( &pdsp->xramp );
5094
5095 // crossfade from previous to current preset gain
5096 if( gain_target > gain_prev )
5097 {
5098 // ramping gain up - ramp up gain to target in last 10% of ramp
5099 float rf = (float)r;
5100 float pmax = (float)PMAX;
5101
5102 rf = rf / pmax; // rf 0->1.0
5103
5104 if( rf < 0.9 ) rf = 0.0;
5105 else rf = (rf - 0.9) / (1.0 - 0.9); // 0->1.0 after rf > 0.9
5106
5107 // crossfade gain from prev to target over rf
5108 gain = gain_prev + (gain_target - gain_prev) * rf;
5109
5110 return gain;
5111 }
5112 else
5113 {
5114 // ramping gain down - drop gain to target in first 10% of ramp
5115 float rf = (float) r;
5116 float pmax = (float)PMAX;
5117
5118 rf = rf / pmax; // rf 0.0->1.0
5119
5120 if( rf < 0.1 ) rf = (rf - 0.1) / (0.0 - 0.1); // 1.0->0.0 if rf < 0.1
5121 else rf = 0.0;
5122
5123 // crossfade gain from prev to target over rf
5124 gain = gain_prev + (gain_target - gain_prev) * (1.0 - rf);
5125
5126 return gain;
5127 }
5128}
5129
5130// free previous preset if not 0
5131inline void DSP_FreePrevPreset( dsp_t *pdsp )
5132{
5133 // free previous presets if non-null - ie: rapid change of preset just kills old without xfade
5134 if( pdsp->ipsetprev )
5135 {
5136 int i;
5137
5138 for( i = 0; i < pdsp->cchan; i++ )
5139 {
5140 if( pdsp->ppsetprev[i] )
5141 {
5142 PSET_Free( pdsp->ppsetprev[i] );
5143 pdsp->ppsetprev[i] = NULL;
5144 }
5145 }
5146 pdsp->ipsetprev = 0;
5147 }
5148
5149}
5150
5151// alloc new preset if different from current
5152// xfade from prev to new preset
5153// free previous preset, copy current into previous, set up xfade from previous to new
5154void DSP_SetPreset( int idsp, int ipsetnew )
5155{
5156 dsp_t *pdsp;
5157 pset_t *ppsetnew[DSPCHANMAX];
5158 int i;
5159
5160 ASSERT( idsp >= 0 && idsp < CDSPS );
5161
5162 pdsp = &dsps[idsp];
5163
5164 // validate new preset range
5165 if( ipsetnew >= CPSETTEMPLATES || ipsetnew < 0 )
5166 return;
5167
5168 // ignore if new preset is same as current preset
5169 if( ipsetnew == pdsp->ipset )
5170 return;
5171
5172 // alloc new presets (each channel is a duplicate preset)
5173 ASSERT( pdsp->cchan <= DSPCHANMAX );
5174
5175 for( i = 0; i < pdsp->cchan; i++ )
5176 {
5177 ppsetnew[i] = PSET_Alloc( ipsetnew );
5178
5179 if( !ppsetnew[i] )
5180 {
5181 Con_Printf ("DSP preset failed to allocate!!!\n");
5182 return;
5183 }
5184 }
5185
5186 ASSERT( pdsp );
5187
5188 // free PREVIOUS previous preset if not 0
5189 DSP_FreePrevPreset( pdsp );
5190
5191 for( i = 0; i < pdsp->cchan; i++ )
5192 {
5193 // current becomes previous
5194 pdsp->ppsetprev[i] = pdsp->ppset[i];
5195
5196 // new becomes current
5197 pdsp->ppset[i] = ppsetnew[i];
5198 }
5199
5200 pdsp->ipsetprev = pdsp->ipset;
5201 pdsp->ipset = ipsetnew;
5202
5203 // clear ramp
5204 RMP_SetEnd( &pdsp->xramp );
5205
5206 // make sure previous dsp preset has data
5207 ASSERT( pdsp->ppsetprev[0] );
5208
5209 // shouldn't be crossfading if current dsp preset == previous dsp preset
5210 ASSERT( pdsp->ipset != pdsp->ipsetprev );
5211
5212 RMP_Init( &pdsp->xramp, pdsp->xfade, 0, PMAX );
5213}
5214
5215///////////////////////////////////////
5216// Helpers: called only from DSP_Process
5217///////////////////////////////////////
5218
5219// return true if batch processing version of preset exists
5220inline qboolean FBatchPreset( pset_t *ppset )
5221{
5222 switch( ppset->type )
5223 {
5224 case PSET_LINEAR:
5225 return true;
5226 case PSET_SIMPLE:
5227 return true;
5228 default:
5229 return false;
5230 }
5231}
5232
5233// Helper: called only from DSP_Process
5234// mix front stereo buffer to mono buffer, apply dsp fx
5235inline void DSP_ProcessStereoToMono( dsp_t *pdsp, portable_samplepair_t *pbfront, int sampleCount, qboolean bcrossfading )
5236{
5237 portable_samplepair_t *pbf = pbfront; // pointer to buffer of front stereo samples to process
5238 int count = sampleCount;
5239 int av, x;
5240
5241 if( !bcrossfading )
5242 {
5243 if( FBatchPreset( pdsp->ppset[0] ))
5244 {
5245 // convert Stereo to Mono in place, then batch process fx: perf KDB
5246
5247 // front->left + front->right / 2 into front->left, front->right duplicated.
5248 while( count-- )
5249 {
5250 pbf->left = (pbf->left + pbf->right) >> 1;
5251 pbf++;
5252 }
5253
5254 // process left (mono), duplicate output into right
5255 PSET_GetNextN( pdsp->ppset[0], pbfront, sampleCount, OP_LEFT_DUPLICATE);
5256 }
5257 else
5258 {
5259 // avg left and right -> mono fx -> duplcate out left and right
5260 while( count-- )
5261 {
5262 av = ( ( pbf->left + pbf->right ) >> 1 );
5263 x = PSET_GetNext( pdsp->ppset[0], av );
5264 x = CLIP_DSP( x );
5265 pbf->left = pbf->right = x;
5266 pbf++;
5267 }
5268 }
5269 return;
5270 }
5271
5272 // crossfading to current preset from previous preset
5273 if( bcrossfading )
5274 {
5275 int r = -1;
5276 int fl, flp;
5277 int xf_fl;
5278
5279 while( count-- )
5280 {
5281 av = ( ( pbf->left + pbf->right ) >> 1 );
5282
5283 // get current preset values
5284 fl = PSET_GetNext( pdsp->ppset[0], av );
5285
5286 // get previous preset values
5287 flp = PSET_GetNext( pdsp->ppsetprev[0], av );
5288
5289 fl = CLIP_DSP(fl);
5290 flp = CLIP_DSP(flp);
5291
5292 // get current ramp value
5293 r = RMP_GetNext( &pdsp->xramp );
5294
5295 // crossfade from previous to current preset
5296 xf_fl = XFADE( fl, flp, r ); // crossfade front left previous to front left
5297
5298 pbf->left = xf_fl; // crossfaded front left, duplicate in right channel
5299 pbf->right = xf_fl;
5300
5301 pbf++;
5302
5303 }
5304
5305 }
5306}
5307
5308// Helper: called only from DSP_Process
5309// DSP_Process stereo in to stereo out (if more than 2 procs, ignore them)
5310inline void DSP_ProcessStereoToStereo( dsp_t *pdsp, portable_samplepair_t *pbfront, int sampleCount, qboolean bcrossfading )
5311{
5312 portable_samplepair_t *pbf = pbfront; // pointer to buffer of front stereo samples to process
5313 int count = sampleCount;
5314 int fl, fr;
5315
5316 if( !bcrossfading )
5317 {
5318
5319 if( FBatchPreset( pdsp->ppset[0] ) && FBatchPreset( pdsp->ppset[1] ))
5320 {
5321 // process left & right
5322 PSET_GetNextN( pdsp->ppset[0], pbfront, sampleCount, OP_LEFT );
5323 PSET_GetNextN( pdsp->ppset[1], pbfront, sampleCount, OP_RIGHT );
5324 }
5325 else
5326 {
5327 // left -> left fx, right -> right fx
5328 while( count-- )
5329 {
5330 fl = PSET_GetNext( pdsp->ppset[0], pbf->left );
5331 fr = PSET_GetNext( pdsp->ppset[1], pbf->right );
5332
5333 fl = CLIP_DSP( fl );
5334 fr = CLIP_DSP( fr );
5335
5336 pbf->left = fl;
5337 pbf->right = fr;
5338 pbf++;
5339 }
5340 }
5341 return;
5342 }
5343
5344 // crossfading to current preset from previous preset
5345 if( bcrossfading )
5346 {
5347 int r, flp, frp;
5348 int xf_fl, xf_fr;
5349
5350 while( count-- )
5351 {
5352 // get current preset values
5353 fl = PSET_GetNext( pdsp->ppset[0], pbf->left );
5354 fr = PSET_GetNext( pdsp->ppset[1], pbf->right );
5355
5356 // get previous preset values
5357 flp = PSET_GetNext( pdsp->ppsetprev[0], pbf->left );
5358 frp = PSET_GetNext( pdsp->ppsetprev[1], pbf->right );
5359
5360 // get current ramp value
5361 r = RMP_GetNext( &pdsp->xramp );
5362
5363 fl = CLIP_DSP( fl );
5364 fr = CLIP_DSP( fr );
5365 flp = CLIP_DSP( flp );
5366 frp = CLIP_DSP( frp );
5367
5368 // crossfade from previous to current preset
5369 xf_fl = XFADE( fl, flp, r ); // crossfade front left previous to front left
5370 xf_fr = XFADE( fr, frp, r );
5371
5372 pbf->left = xf_fl; // crossfaded front left
5373 pbf->right = xf_fr;
5374
5375 pbf++;
5376 }
5377 }
5378}
5379
5380void DSP_ClearState( void )
5381{
5382 if( !dsp_room.value ) return; // not init
5383
5384 Cvar_SetValue ( "dsp_room", 0.0f ); //FIXME
5385 Cvar_SetValue ( "dsp_room_type", 0.0f );
5386
5387 CheckNewDspPresets();
5388
5389 // don't crossfade
5390 dsps[0].xramp.fhitend = true;
5391}
5392
5393// Main DSP processing routine:
5394// process samples in buffers using pdsp processor
5395// continue crossfade between 2 dsp processors if crossfading on switch
5396// pfront - front stereo buffer to process
5397// prear - rear stereo buffer to process (may be NULL)
5398// sampleCount - number of samples in pbuf to process
5399// This routine also maps the # processing channels in the pdsp to the number of channels
5400// supplied. ie: if the pdsp has 4 channels and pbfront and pbrear are both non-null, the channels
5401// map 1:1 through the processors.
5402
5403void DSP_Process( int idsp, portable_samplepair_t *pbfront, int sampleCount )
5404{
5405 qboolean bcrossfading;
5406 int cprocs; // output cannels (1, 2 or 4)
5407 dsp_t *pdsp;
5408
5409 if( idsp < 0 || idsp >= CDSPS )
5410 return;
5411
5412 ASSERT ( idsp < CDSPS ); // make sure idsp is valid
5413
5414 pdsp = &dsps[idsp];
5415
5416 // if current and previous preset 0, return - preset 0 is 'off'
5417 if( !pdsp->ipset && !pdsp->ipsetprev )
5418 return;
5419
5420 ASSERT( pbfront );
5421
5422 // return right away if fx processing is turned off
5423 if( dsp_off.value )
5424 return;
5425
5426 if( sampleCount < 0 )
5427 return;
5428
5429 bcrossfading = !RMP_HitEnd( &pdsp->xramp );
5430
5431 // if not crossfading, and previous channel is not null, free previous
5432 if( !bcrossfading ) DSP_FreePrevPreset( pdsp );
5433
5434 cprocs = pdsp->cchan;
5435
5436 // NOTE: when mixing between different channel sizes,
5437 // always AVERAGE down to fewer channels and DUPLICATE up more channels.
5438 // The following routines always process cchan_in channels.
5439 // ie: QuadToMono still updates 4 values in buffer
5440
5441 // DSP_Process stereo in to mono out (ie: left and right are averaged)
5442 if( cprocs == 1 )
5443 {
5444 DSP_ProcessStereoToMono( pdsp, pbfront, sampleCount, bcrossfading );
5445 return;
5446 }
5447
5448 // DSP_Process stereo in to stereo out (if more than 2 procs, ignore them)
5449 if( cprocs >= 2 )
5450 {
5451 DSP_ProcessStereoToStereo( pdsp, pbfront, sampleCount, bcrossfading );
5452 return;
5453 }
5454}
5455
5456// DSP helpers
5457
5458// free all dsp processors
5459void FreeDsps( void )
5460{
5461 DSP_Free( idsp_room );
5462 idsp_room = 0;
5463
5464 DSP_FreeAll();
5465}
5466
5467// alloc dsp processors
5468qboolean AllocDsps( void )
5469{
5470 DSP_InitAll();
5471
5472 idsp_room = -1.0;
5473
5474 // initialize DSP cvars
5475 Cvar_RegisterVariable (&dsp_room);
5476 Cvar_RegisterVariable (&dsp_room_type);
5477 Cvar_RegisterVariable (&dsp_stereo);
5478 Cvar_RegisterVariable (&dsp_off);
5479
5480 // alloc dsp room channel (mono, stereo if dsp_stereo is 1)
5481
5482 // dsp room is mono, 300ms fade time
5483 idsp_room = DSP_Alloc( dsp_room.value, 300, dsp_stereo.value * 2 );
5484
5485 // init prev values
5486 ipset_room_prev = dsp_room.value;
5487 ipset_room_typeprev = dsp_room_type.value;
5488
5489 if( idsp_room < 0 )
5490 {
5491 Con_DPrintf ("DSP processor failed to initialize!\n");
5492
5493 FreeDsps();
5494 return false;
5495 }
5496 return true;
5497}
5498
5499
5500// Helper to check for change in preset of any of 4 processors
5501// if switching to a new preset, alloc new preset, simulate both presets in DSP_Process & xfade,
5502void CheckNewDspPresets( void )
5503{
5504 int iroomtype = dsp_room_type.value;
5505 int iroom;
5506
5507 if( dsp_off.value )
5508 return;
5509 /*
5510 if( s_listener.waterlevel > 2 )
5511 iroom = 15;
5512 else if( s_listener.inmenu )
5513 iroom = 0;
5514 else iroom = dsp_room.value;
5515 */
5516 iroom = dsp_room.value;
5517
5518 // legacy code support for "room_type" Cvar
5519 if( iroomtype != ipset_room_typeprev )
5520 {
5521 // force dsp_room = room_type
5522 ipset_room_typeprev = iroomtype;
5523 Cvar_SetValue ( "dsp_room", iroomtype );
5524 }
5525
5526 if( iroom != ipset_room_prev )
5527 {
5528 DSP_SetPreset( idsp_room, iroom );
5529 ipset_room_prev = iroom;
5530
5531 // force room_type = dsp_room
5532 Cvar_SetValue ( "dsp_room_type", iroom );
5533 ipset_room_typeprev = iroom;
5534 }
5535}