├── test.raw ├── README.md ├── EAXsoft.depend ├── EAXsoft.cbp ├── src ├── main.cpp └── ReverbEffect.cpp ├── include └── ReverbEffect.h └── LICENSE /test.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relfos/EAXReverb/HEAD/test.raw -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EAXReverb 2 | ============ 3 | 4 | _EAXReverb_ implements a audio environment reverb effect DSP (EAX/EFX) in C++. 5 | This code was based in the OpenALsoft C implementation of the EAX reverb algorithm. 6 | 7 | A sample project is included: 8 | * Takes a input file that contains audio in raw format (16-bit, mono, 44100hz) 9 | * Outputs another raw audio file, this time in stereo, and with added EAX reverb. 10 | -------------------------------------------------------------------------------- /EAXsoft.depend: -------------------------------------------------------------------------------- 1 | # depslib dependency file v1.0 2 | 1441258529 source:d:\code\eax_emu\eaxsoft\main.cpp 3 | 4 | 5 | 6 | 7 | 8 | "ReverbEffect.h" 9 | 10 | 1441258424 d:\code\eax_emu\eaxsoft\include\reverbeffect.h 11 | 12 | 13 | 1441258936 source:d:\code\eax_emu\eaxsoft\src\reverbeffect.cpp 14 | "ReverbEffect.h" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 1441367060 source:d:\code\eaxreverb\src\reverbeffect.cpp 23 | "ReverbEffect.h" 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 1441371790 d:\code\eaxreverb\include\reverbeffect.h 32 | 33 | 34 | 1441371780 source:d:\code\eaxreverb\src\main.cpp 35 | 36 | 37 | 38 | 39 | 40 | "ReverbEffect.h" 41 | 42 | -------------------------------------------------------------------------------- /EAXsoft.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 47 | 48 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ReverbEffect.h" 8 | 9 | int main() 10 | { 11 | int sampleCount = 0; 12 | int frequency = 44100; 13 | 14 | 15 | printf("Loading raw samples (only 16-bit, %d mono supported in this program)\n" ,frequency); 16 | FILE *fp = fopen("test.raw","rb"); 17 | if( fp == NULL ) 18 | { 19 | perror("Error while opening the file...\n"); 20 | exit(EXIT_FAILURE); 21 | } 22 | 23 | // get file size 24 | fseek(fp, 0L, SEEK_END); 25 | int size = ftell(fp); 26 | 27 | // seek back to beginning 28 | fseek(fp, 0L, SEEK_SET); 29 | 30 | // calculate number of samples (assune 16-bit, mono, so, 2 bytes per sample) 31 | 32 | sampleCount = size / 2; 33 | int16_t *samples = new int16_t[sampleCount]; 34 | fread(samples, 1, size, fp); 35 | fclose(fp); 36 | 37 | printf("Converting now samples...\n"); 38 | float *floatSamplesIn = new float[sampleCount]; 39 | 40 | // now convert samples into floats 41 | int i; 42 | for (i=0; isampleCount) 87 | { 88 | workSamples = sampleCount; 89 | } 90 | 91 | effect.Process(workSamples, &floatSamplesIn[offset], floatSamplesOut); 92 | 93 | for (i=0; i0); 105 | 106 | delete[] floatSamplesIn; 107 | 108 | 109 | fclose(fp); 110 | 111 | printf("Done!\n"); 112 | 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /include/ReverbEffect.h: -------------------------------------------------------------------------------- 1 | #ifndef REVERBEFFECT_H 2 | #define REVERBEFFECT_H 3 | 4 | #include 5 | 6 | #define BOOL int 7 | 8 | #define OUTPUT_CHANNELS 2 9 | #define TRUE 1 10 | #define FALSE 0 11 | #define REVERB_BUFFERSIZE (2048u) 12 | #define F_2PI (6.28318530717958647692f) 13 | #define FLT_EPSILON 1.19209290E-07F 14 | 15 | #define MAX_AMBI_COEFFS 4 16 | 17 | #define SPEEDOFSOUNDMETRESPERSEC 343.3f 18 | 19 | #define GAIN_SILENCE_THRESHOLD 0.00001f 20 | 21 | /* This is a user config option for modifying the overall output of the reverb effect. */ 22 | #define ReverbBoost 1.0f 23 | 24 | /* Effect parameter ranges and defaults. */ 25 | #define EAXREVERB_MIN_DENSITY (0.0f) 26 | #define EAXREVERB_MAX_DENSITY (1.0f) 27 | #define EAXREVERB_DEFAULT_DENSITY (1.0f) 28 | 29 | #define EAXREVERB_MIN_DIFFUSION (0.0f) 30 | #define EAXREVERB_MAX_DIFFUSION (1.0f) 31 | #define EAXREVERB_DEFAULT_DIFFUSION (1.0f) 32 | 33 | #define EAXREVERB_MIN_GAIN (0.0f) 34 | #define EAXREVERB_MAX_GAIN (1.0f) 35 | #define EAXREVERB_DEFAULT_GAIN (0.32f) 36 | 37 | #define EAXREVERB_MIN_GAINHF (0.0f) 38 | #define EAXREVERB_MAX_GAINHF (1.0f) 39 | #define EAXREVERB_DEFAULT_GAINHF (0.89f) 40 | 41 | #define EAXREVERB_MIN_GAINLF (0.0f) 42 | #define EAXREVERB_MAX_GAINLF (1.0f) 43 | #define EAXREVERB_DEFAULT_GAINLF (1.0f) 44 | 45 | #define EAXREVERB_MIN_DECAY_TIME (0.1f) 46 | #define EAXREVERB_MAX_DECAY_TIME (20.0f) 47 | #define EAXREVERB_DEFAULT_DECAY_TIME (1.49f) 48 | 49 | #define EAXREVERB_MIN_DECAY_HFRATIO (0.1f) 50 | #define EAXREVERB_MAX_DECAY_HFRATIO (2.0f) 51 | #define EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f) 52 | 53 | #define EAXREVERB_MIN_DECAY_LFRATIO (0.1f) 54 | #define EAXREVERB_MAX_DECAY_LFRATIO (2.0f) 55 | #define EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f) 56 | 57 | #define EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f) 58 | #define EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f) 59 | #define EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) 60 | 61 | #define EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f) 62 | #define EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f) 63 | #define EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) 64 | 65 | #define EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f) 66 | 67 | #define EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f) 68 | #define EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f) 69 | #define EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) 70 | 71 | #define EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f) 72 | #define EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f) 73 | #define EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) 74 | 75 | #define EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f) 76 | 77 | #define EAXREVERB_MIN_ECHO_TIME (0.075f) 78 | #define EAXREVERB_MAX_ECHO_TIME (0.25f) 79 | #define EAXREVERB_DEFAULT_ECHO_TIME (0.25f) 80 | 81 | #define EAXREVERB_MIN_ECHO_DEPTH (0.0f) 82 | #define EAXREVERB_MAX_ECHO_DEPTH (1.0f) 83 | #define EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f) 84 | 85 | #define EAXREVERB_MIN_MODULATION_TIME (0.04f) 86 | #define EAXREVERB_MAX_MODULATION_TIME (4.0f) 87 | #define EAXREVERB_DEFAULT_MODULATION_TIME (0.25f) 88 | 89 | #define EAXREVERB_MIN_MODULATION_DEPTH (0.0f) 90 | #define EAXREVERB_MAX_MODULATION_DEPTH (1.0f) 91 | #define EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f) 92 | 93 | #define EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) 94 | #define EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) 95 | #define EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) 96 | 97 | #define EAXREVERB_MIN_HFREFERENCE (1000.0f) 98 | #define EAXREVERB_MAX_HFREFERENCE (20000.0f) 99 | #define EAXREVERB_DEFAULT_HFREFERENCE (5000.0f) 100 | 101 | #define EAXREVERB_MIN_LFREFERENCE (20.0f) 102 | #define EAXREVERB_MAX_LFREFERENCE (1000.0f) 103 | #define EAXREVERB_DEFAULT_LFREFERENCE (250.0f) 104 | 105 | #define EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) 106 | #define EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) 107 | #define EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) 108 | 109 | #define EAXREVERB_MIN_DECAY_HFLIMIT FALSE 110 | #define EAXREVERB_MAX_DECAY_HFLIMIT TRUE 111 | #define EAXREVERB_DEFAULT_DECAY_HFLIMIT TRUE 112 | 113 | typedef enum FilterType { 114 | /** EFX-style low-pass filter, specifying a gain and reference frequency. */ 115 | Filter_HighShelf, 116 | /** EFX-style high-pass filter, specifying a gain and reference frequency. */ 117 | Filter_LowShelf, 118 | } FilterType; 119 | 120 | typedef struct 121 | { 122 | // The delay lines use sample lengths that are powers of 2 to allow the 123 | // use of bit-masking instead of a modulus for wrapping. 124 | uint32_t Mask; 125 | float *Line; 126 | } DelayLine; 127 | 128 | typedef struct { 129 | float x[2]; /* History of two last input samples */ 130 | float y[2]; /* History of two last output samples */ 131 | float a[3]; /* Transfer function coefficients "a" */ 132 | float b[3]; /* Transfer function coefficients "b" */ 133 | } FilterState; 134 | 135 | typedef struct { 136 | // Shared Reverb Properties 137 | float Density; 138 | float Diffusion; 139 | float Gain; 140 | float GainHF; 141 | float DecayTime; 142 | float DecayHFRatio; 143 | float ReflectionsGain; 144 | float ReflectionsDelay; 145 | float LateReverbGain; 146 | float LateReverbDelay; 147 | float AirAbsorptionGainHF; 148 | float RoomRolloffFactor; 149 | BOOL DecayHFLimit; 150 | 151 | // Additional EAX Reverb Properties 152 | float GainLF; 153 | float DecayLFRatio; 154 | float ReflectionsPan[3]; 155 | float LateReverbPan[3]; 156 | float EchoTime; 157 | float EchoDepth; 158 | float ModulationTime; 159 | float ModulationDepth; 160 | float HFReference; 161 | float LFReference; 162 | 163 | } ReverbSettings; 164 | 165 | typedef struct { 166 | // Modulator delay line. 167 | DelayLine Delay; 168 | 169 | // The vibrato time is tracked with an index over a modulus-wrapped 170 | // range (in samples). 171 | uint32_t Index; 172 | uint32_t Range; 173 | 174 | // The depth of frequency change (also in samples) and its filter. 175 | float Depth; 176 | float Coeff; 177 | float Filter; 178 | } Modulator; 179 | 180 | typedef struct { 181 | // Output gain for early reflections. 182 | float Gain; 183 | 184 | // Early reflections are done with 4 delay lines. 185 | float Coeff[4]; 186 | DelayLine Delay[4]; 187 | uint32_t Offset[4]; 188 | 189 | // The gain for each output channel based on 3D panning (only for the 190 | // EAX path). 191 | float PanGain[OUTPUT_CHANNELS]; 192 | } EarlyDelay; 193 | 194 | typedef struct { 195 | // Output gain for late reverb. 196 | float Gain; 197 | 198 | // Attenuation to compensate for the modal density and decay rate of 199 | // the late lines. 200 | float DensityGain; 201 | 202 | // The feed-back and feed-forward all-pass coefficient. 203 | float ApFeedCoeff; 204 | 205 | // Mixing matrix coefficient. 206 | float MixCoeff; 207 | 208 | // Late reverb has 4 parallel all-pass filters. 209 | float ApCoeff[4]; 210 | DelayLine ApDelay[4]; 211 | uint32_t ApOffset[4]; 212 | 213 | // In addition to 4 cyclical delay lines. 214 | float Coeff[4]; 215 | DelayLine Delay[4]; 216 | uint32_t Offset[4]; 217 | 218 | // The cyclical delay lines are 1-pole low-pass filtered. 219 | float LpCoeff[4]; 220 | float LpSample[4]; 221 | 222 | // The gain for each output channel based on 3D panning (only for the 223 | // EAX path). 224 | float PanGain[OUTPUT_CHANNELS]; 225 | } LateDelay; 226 | 227 | typedef struct { 228 | // Attenuation to compensate for the modal density and decay rate of 229 | // the echo line. 230 | float DensityGain; 231 | 232 | // Echo delay and all-pass lines. 233 | DelayLine Delay; 234 | DelayLine ApDelay; 235 | 236 | float Coeff; 237 | float ApFeedCoeff; 238 | float ApCoeff; 239 | 240 | uint32_t Offset; 241 | uint32_t ApOffset; 242 | 243 | // The echo line is 1-pole low-pass filtered. 244 | float LpCoeff; 245 | float LpSample; 246 | 247 | // Echo mixing coefficients. 248 | float MixCoeff[2]; 249 | } ReverbEcho; 250 | 251 | class ReverbEffect 252 | { 253 | public: 254 | ReverbEffect(uint32_t frequency); 255 | virtual ~ReverbEffect(); 256 | 257 | void Process(uint32_t SamplesToDo, const float *SamplesIn, float *SamplesOut); 258 | void Update(int frequency); 259 | 260 | void LoadPreset(int environment, float environmentSize, float environmentDiffusion, int room, int roomHF, int roomLF, 261 | float decayTime, float decayHFRatio, float decayLFRatio, 262 | int reflections, float reflectionsDelay, float reflectionsPanX, float reflectionsPanY, float reflectionsPanZ, 263 | int reverb, float reverbDelay, float reverbPanX, float reverbPanY, float reverbPanZ, 264 | float echoTime, float echoDepth, float modulationTime, float modulationDepth, float airAbsorptionHF, 265 | float hfReference, float lfReference, float roomRolloffFactor, int flags); 266 | 267 | 268 | private: 269 | ReverbSettings settings; 270 | 271 | // All delay lines are allocated as a single buffer to reduce memory 272 | // fragmentation and management code. 273 | float *SampleBuffer; 274 | uint32_t TotalSamples; 275 | 276 | // Master effect filters 277 | FilterState LpFilter; 278 | FilterState HpFilter; // EAX only 279 | 280 | Modulator Mod; 281 | 282 | // Initial effect delay. 283 | DelayLine Delay; 284 | // The tap points for the initial delay. First tap goes to early 285 | // reflections, the last to late reverb. 286 | uint32_t DelayTap[2]; 287 | 288 | EarlyDelay Early; 289 | 290 | // Decorrelator delay line. 291 | DelayLine Decorrelator; 292 | // There are actually 4 decorrelator taps, but the first occurs at the 293 | // initial sample. 294 | uint32_t DecoTap[3]; 295 | 296 | LateDelay Late; 297 | 298 | ReverbEcho Echo; 299 | 300 | // The current read offset for all delay lines. 301 | uint32_t Offset; 302 | 303 | /* Temporary storage used when processing, before deinterlacing. */ 304 | float ReverbSamples[REVERB_BUFFERSIZE][4]; 305 | float EarlySamples[REVERB_BUFFERSIZE][4]; 306 | 307 | float ambiCoeffs[OUTPUT_CHANNELS][MAX_AMBI_COEFFS]; 308 | 309 | void ComputeAmbientGains(float ingain, float gains[OUTPUT_CHANNELS]); 310 | void ComputeDirectionalGains(const float dir[3], float ingain, float gains[OUTPUT_CHANNELS]); 311 | 312 | void AllocLines(uint32_t frequency); 313 | 314 | inline float EAXModulation(float in); 315 | 316 | inline float EarlyDelayLineOut(uint32_t index); 317 | inline void EarlyReflection(float in, float *out); 318 | 319 | inline float LateAllPassInOut(uint32_t index, float in); 320 | inline float LateDelayLineOut(uint32_t index); 321 | 322 | inline float LateLowPassInOut(uint32_t index, float in); 323 | inline void LateReverb(const float *in, float *out); 324 | 325 | inline void EAXEcho(float in, float *late); 326 | inline void EAXVerbPass(float in, float *early, float *late); 327 | 328 | void UpdateDelayLine(float earlyDelay, float lateDelay, uint32_t frequency); 329 | void UpdateModulator(float modTime, float modDepth, uint32_t frequency); 330 | void UpdateEarlyLines(float reverbGain, float earlyGain, float lateDelay); 331 | void UpdateDecorrelator(float density, uint32_t frequency); 332 | void UpdateLateLines(float reverbGain, float lateGain, float xMix, float density, float decayTime, float diffusion, float hfRatio, float cw, uint32_t frequency); 333 | void UpdateEchoLine(float reverbGain, float lateGain, float echoTime, float decayTime, float diffusion, float echoDepth, float hfRatio, float cw, uint32_t frequency); 334 | 335 | void Update3DPanning(const float *ReflectionsPan, const float *LateReverbPan, float Gain); 336 | }; 337 | 338 | #endif // REVERBEFFECT_H 339 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /src/ReverbEffect.cpp: -------------------------------------------------------------------------------- 1 | #include "ReverbEffect.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | 12 | /* This coefficient is used to define the maximum frequency range controlled 13 | * by the modulation depth. The current value of 0.1 will allow it to swing 14 | * from 0.9x to 1.1x. This value must be below 1. At 1 it will cause the 15 | * sampler to stall on the downswing, and above 1 it will cause it to sample 16 | * backwards. 17 | */ 18 | static const float MODULATION_DEPTH_COEFF = 0.1f; 19 | 20 | /* A filter is used to avoid the terrible distortion caused by changing 21 | * modulation time and/or depth. To be consistent across different sample 22 | * rates, the coefficient must be raised to a constant divided by the sample 23 | * rate: coeff^(constant / rate). 24 | */ 25 | static const float MODULATION_FILTER_COEFF = 0.048f; 26 | static const float MODULATION_FILTER_CONST = 100000.0f; 27 | 28 | // When diffusion is above 0, an all-pass filter is used to take the edge off 29 | // the echo effect. It uses the following line length (in seconds). 30 | static const float ECHO_ALLPASS_LENGTH = 0.0133f; 31 | 32 | // Input into the late reverb is decorrelated between four channels. Their 33 | // timings are dependent on a fraction and multiplier. See the 34 | // UpdateDecorrelator() routine for the calculations involved. 35 | static const float DECO_FRACTION = 0.15f; 36 | static const float DECO_MULTIPLIER = 2.0f; 37 | 38 | // All delay line lengths are specified in seconds. 39 | 40 | // The lengths of the early delay lines. 41 | static const float EARLY_LINE_LENGTH[4] = 42 | { 43 | 0.0015f, 0.0045f, 0.0135f, 0.0405f 44 | }; 45 | 46 | // The lengths of the late all-pass delay lines. 47 | static const float ALLPASS_LINE_LENGTH[4] = 48 | { 49 | 0.0151f, 0.0167f, 0.0183f, 0.0200f, 50 | }; 51 | 52 | // The lengths of the late cyclical delay lines. 53 | static const float LATE_LINE_LENGTH[4] = 54 | { 55 | 0.0211f, 0.0311f, 0.0461f, 0.0680f 56 | }; 57 | 58 | // The late cyclical delay lines have a variable length dependent on the 59 | // effect's density parameter (inverted for some reason) and this multiplier. 60 | static const float LATE_LINE_MULTIPLIER = 4.0f; 61 | 62 | 63 | 64 | inline float lerp(float val1, float val2, float mu) 65 | { 66 | return val1 + (val2-val1)*mu; 67 | } 68 | 69 | /* Find the next power-of-2 for non-power-of-2 numbers. */ 70 | inline uint32_t NextPowerOf2(uint32_t value) 71 | { 72 | if(value > 0) 73 | { 74 | value--; 75 | value |= value>>1; 76 | value |= value>>2; 77 | value |= value>>4; 78 | value |= value>>8; 79 | value |= value>>16; 80 | } 81 | return value+1; 82 | } 83 | 84 | inline uint32_t Truncate(float f) 85 | { 86 | return (uint32_t)f; 87 | } 88 | 89 | inline float minf(float a, float b) 90 | { return ((a > b) ? b : a); } 91 | 92 | inline float maxf(float a, float b) 93 | { return ((a > b) ? a : b); } 94 | 95 | inline float clampf(float val, float min, float max) 96 | { return minf(max, maxf(min, val)); } 97 | 98 | inline uint32_t maxu(uint32_t a, uint32_t b) 99 | { return ((a > b) ? a : b); } 100 | 101 | 102 | inline float FilterState_Process(FilterState *filter, float sample) 103 | { 104 | float outsmp; 105 | 106 | outsmp = filter->b[0] * sample + 107 | filter->b[1] * filter->x[0] + 108 | filter->b[2] * filter->x[1] - 109 | filter->a[1] * filter->y[0] - 110 | filter->a[2] * filter->y[1]; 111 | filter->x[1] = filter->x[0]; 112 | filter->x[0] = sample; 113 | filter->y[1] = filter->y[0]; 114 | filter->y[0] = outsmp; 115 | 116 | return outsmp; 117 | } 118 | 119 | void FilterState_clear(FilterState *filter) 120 | { 121 | filter->x[0] = 0.0f; 122 | filter->x[1] = 0.0f; 123 | filter->y[0] = 0.0f; 124 | filter->y[1] = 0.0f; 125 | } 126 | 127 | void FilterState_setParams(FilterState *filter, FilterType type, float gain, float freq_mult, float bandwidth) 128 | { 129 | float alpha; 130 | float w0; 131 | 132 | // Limit gain to -100dB 133 | gain = maxf(gain, 0.00001f); 134 | 135 | w0 = F_2PI * freq_mult; 136 | 137 | /* Calculate filter coefficients depending on filter type */ 138 | switch(type) 139 | { 140 | case Filter_HighShelf: 141 | alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f); 142 | filter->b[0] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha); 143 | filter->b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cosf(w0) ); 144 | filter->b[2] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha); 145 | filter->a[0] = (gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha; 146 | filter->a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cosf(w0) ); 147 | filter->a[2] = (gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha; 148 | break; 149 | case Filter_LowShelf: 150 | alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f); 151 | filter->b[0] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha); 152 | filter->b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cosf(w0) ); 153 | filter->b[2] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha); 154 | filter->a[0] = (gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha; 155 | filter->a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cosf(w0) ); 156 | filter->a[2] = (gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha; 157 | break; 158 | } 159 | 160 | filter->b[2] /= filter->a[0]; 161 | filter->b[1] /= filter->a[0]; 162 | filter->b[0] /= filter->a[0]; 163 | filter->a[2] /= filter->a[0]; 164 | filter->a[1] /= filter->a[0]; 165 | filter->a[0] /= filter->a[0]; 166 | } 167 | 168 | void ReverbEffect::ComputeAmbientGains(float ingain, float gains[OUTPUT_CHANNELS]) 169 | { 170 | uint32_t i; 171 | 172 | for(i = 0;i < OUTPUT_CHANNELS;i++) 173 | { 174 | // The W coefficients are based on a mathematical average of the 175 | // output, scaled by sqrt(2) to compensate for FuMa-style Ambisonics 176 | // scaling the W channel input by sqrt(0.5). The square root of the 177 | // base average provides for a more perceptual average volume, better 178 | // suited to non-directional gains. 179 | gains[i] = sqrtf(ambiCoeffs[i][0]/1.4142f) * ingain; 180 | } 181 | } 182 | 183 | void ReverbEffect::ComputeDirectionalGains(const float dir[3], float ingain, float gains[OUTPUT_CHANNELS]) 184 | { 185 | float coeffs[MAX_AMBI_COEFFS]; 186 | uint32_t i, j; 187 | /* Convert from OpenAL coords to Ambisonics. */ 188 | float x = -dir[2]; 189 | float y = -dir[0]; 190 | float z = dir[1]; 191 | 192 | /* Zeroth-order */ 193 | coeffs[0] = 0.7071f; /* W = sqrt(1.0 / 2.0) */ 194 | /* First-order */ 195 | coeffs[1] = y; /* Y = Y */ 196 | coeffs[2] = z; /* Z = Z */ 197 | coeffs[3] = x; /* X = X */ 198 | 199 | for(i = 0;i < OUTPUT_CHANNELS;i++) 200 | { 201 | float gain = 0.0f; 202 | for(j = 0;j < MAX_AMBI_COEFFS;j++) 203 | gain += ambiCoeffs[i][j]*coeffs[j]; 204 | gains[i] = gain * ingain; 205 | } 206 | } 207 | 208 | 209 | // Basic delay line input/output routines. 210 | static inline float DelayLineOut(DelayLine *Delay, uint32_t offset) 211 | { 212 | return Delay->Line[offset&Delay->Mask]; 213 | } 214 | 215 | static inline void DelayLineIn(DelayLine *Delay, uint32_t offset, float in) 216 | { 217 | Delay->Line[offset&Delay->Mask] = in; 218 | } 219 | 220 | // Attenuated delay line output routine. 221 | static inline float AttenuatedDelayLineOut(DelayLine *Delay, uint32_t offset, float coeff) 222 | { 223 | return coeff * Delay->Line[offset&Delay->Mask]; 224 | } 225 | 226 | // Basic attenuated all-pass input/output routine. 227 | static inline float AllpassInOut(DelayLine *Delay, uint32_t outOffset, uint32_t inOffset, float in, float feedCoeff, float coeff) 228 | { 229 | float out, feed; 230 | 231 | out = DelayLineOut(Delay, outOffset); 232 | feed = feedCoeff * in; 233 | DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in); 234 | 235 | // The time-based attenuation is only applied to the delay output to 236 | // keep it from affecting the feed-back path (which is already controlled 237 | // by the all-pass feed coefficient). 238 | return (coeff * out) - feed; 239 | } 240 | 241 | // Given an input sample, this function produces modulation for the late reverb. 242 | inline float ReverbEffect::EAXModulation(float in) 243 | { 244 | float sinus, frac; 245 | uint32_t offset; 246 | float out0, out1; 247 | 248 | // Calculate the sinus rythm (dependent on modulation time and the 249 | // sampling rate). The center of the sinus is moved to reduce the delay 250 | // of the effect when the time or depth are low. 251 | sinus = 1.0f - cosf(F_2PI * this->Mod.Index / this->Mod.Range); 252 | 253 | // The depth determines the range over which to read the input samples 254 | // from, so it must be filtered to reduce the distortion caused by even 255 | // small parameter changes. 256 | this->Mod.Filter = lerp(this->Mod.Filter, this->Mod.Depth, 257 | this->Mod.Coeff); 258 | 259 | // Calculate the read offset and fraction between it and the next sample. 260 | frac = (1.0f + (this->Mod.Filter * sinus)); 261 | offset = Truncate(frac); 262 | frac -= offset; 263 | 264 | // Get the two samples crossed by the offset, and feed the delay line 265 | // with the next input sample. 266 | out0 = DelayLineOut(&this->Mod.Delay, this->Offset - offset); 267 | out1 = DelayLineOut(&this->Mod.Delay, this->Offset - offset - 1); 268 | DelayLineIn(&this->Mod.Delay, this->Offset, in); 269 | 270 | // Step the modulation index forward, keeping it bound to its range. 271 | this->Mod.Index = (this->Mod.Index + 1) % this->Mod.Range; 272 | 273 | // The output is obtained by linearly interpolating the two samples that 274 | // were acquired above. 275 | return lerp(out0, out1, frac); 276 | } 277 | 278 | // Delay line output routine for early reflections. 279 | inline float ReverbEffect::EarlyDelayLineOut(uint32_t index) 280 | { 281 | return AttenuatedDelayLineOut(&this->Early.Delay[index], this->Offset - this->Early.Offset[index], this->Early.Coeff[index]); 282 | } 283 | 284 | // Given an input sample, this function produces four-channel output for the early reflections. 285 | inline void ReverbEffect::EarlyReflection(float in, float *out) 286 | { 287 | float d[4], v, f[4]; 288 | 289 | // Obtain the decayed results of each early delay line. 290 | d[0] = this->EarlyDelayLineOut(0); 291 | d[1] = this->EarlyDelayLineOut(1); 292 | d[2] = this->EarlyDelayLineOut(2); 293 | d[3] = this->EarlyDelayLineOut(3); 294 | 295 | /* The following uses a lossless scattering junction from waveguide 296 | * theory. It actually amounts to a householder mixing matrix, which 297 | * will produce a maximally diffuse response, and means this can probably 298 | * be considered a simple feed-back delay network (FDN). 299 | * N 300 | * --- 301 | * \ 302 | * v = 2/N / d_i 303 | * --- 304 | * i=1 305 | */ 306 | v = (d[0] + d[1] + d[2] + d[3]) * 0.5f; 307 | // The junction is loaded with the input here. 308 | v += in; 309 | 310 | // Calculate the feed values for the delay lines. 311 | f[0] = v - d[0]; 312 | f[1] = v - d[1]; 313 | f[2] = v - d[2]; 314 | f[3] = v - d[3]; 315 | 316 | // Re-feed the delay lines. 317 | DelayLineIn(&this->Early.Delay[0], this->Offset, f[0]); 318 | DelayLineIn(&this->Early.Delay[1], this->Offset, f[1]); 319 | DelayLineIn(&this->Early.Delay[2], this->Offset, f[2]); 320 | DelayLineIn(&this->Early.Delay[3], this->Offset, f[3]); 321 | 322 | // Output the results of the junction for all four channels. 323 | out[0] = this->Early.Gain * f[0]; 324 | out[1] = this->Early.Gain * f[1]; 325 | out[2] = this->Early.Gain * f[2]; 326 | out[3] = this->Early.Gain * f[3]; 327 | } 328 | 329 | 330 | // All-pass input/output routine for late reverb. 331 | inline float ReverbEffect::LateAllPassInOut(uint32_t index, float in) 332 | { 333 | return AllpassInOut(&this->Late.ApDelay[index], this->Offset - this->Late.ApOffset[index], this->Offset, in, this->Late.ApFeedCoeff, this->Late.ApCoeff[index]); 334 | } 335 | 336 | // Delay line output routine for late reverb. 337 | inline float ReverbEffect::LateDelayLineOut(uint32_t index) 338 | { 339 | return AttenuatedDelayLineOut(&this->Late.Delay[index], this->Offset - this->Late.Offset[index], this->Late.Coeff[index]); 340 | } 341 | 342 | // Low-pass filter input/output routine for late reverb. 343 | inline float ReverbEffect::LateLowPassInOut(uint32_t index, float in) 344 | { 345 | in = lerp(in, this->Late.LpSample[index], this->Late.LpCoeff[index]); 346 | this->Late.LpSample[index] = in; 347 | return in; 348 | } 349 | 350 | // Given four decorrelated input samples, this function produces four-channel output for the late reverb. 351 | inline void ReverbEffect::LateReverb(const float *in, float *out) 352 | { 353 | float d[4], f[4]; 354 | 355 | // Obtain the decayed results of the cyclical delay lines, and add the 356 | // corresponding input channels. Then pass the results through the 357 | // low-pass filters. 358 | 359 | // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and back 360 | // to 0. 361 | d[0] = this->LateLowPassInOut(2, in[2] + this->LateDelayLineOut(2)); 362 | d[1] = this->LateLowPassInOut(0, in[0] + this->LateDelayLineOut(0)); 363 | d[2] = this->LateLowPassInOut(3, in[3] + this->LateDelayLineOut(3)); 364 | d[3] = this->LateLowPassInOut(1, in[1] + this->LateDelayLineOut(1)); 365 | 366 | // To help increase diffusion, run each line through an all-pass filter. 367 | // When there is no diffusion, the shortest all-pass filter will feed the 368 | // shortest delay line. 369 | d[0] = this->LateAllPassInOut(0, d[0]); 370 | d[1] = this->LateAllPassInOut(1, d[1]); 371 | d[2] = this->LateAllPassInOut(2, d[2]); 372 | d[3] = this->LateAllPassInOut(3, d[3]); 373 | 374 | /* Late reverb is done with a modified feed-back delay network (FDN) 375 | * topology. Four input lines are each fed through their own all-pass 376 | * filter and then into the mixing matrix. The four outputs of the 377 | * mixing matrix are then cycled back to the inputs. Each output feeds 378 | * a different input to form a circlular feed cycle. 379 | * 380 | * The mixing matrix used is a 4D skew-symmetric rotation matrix derived 381 | * using a single unitary rotational parameter: 382 | * 383 | * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2 384 | * [ -a, d, c, -b ] 385 | * [ -b, -c, d, a ] 386 | * [ -c, b, -a, d ] 387 | * 388 | * The rotation is constructed from the effect's diffusion parameter, 389 | * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y 390 | * with differing signs, and d is the coefficient x. The matrix is thus: 391 | * 392 | * [ x, y, -y, y ] n = sqrt(matrix_order - 1) 393 | * [ -y, x, y, y ] t = diffusion_parameter * atan(n) 394 | * [ y, -y, x, y ] x = cos(t) 395 | * [ -y, -y, -y, x ] y = sin(t) / n 396 | * 397 | * To reduce the number of multiplies, the x coefficient is applied with 398 | * the cyclical delay line coefficients. Thus only the y coefficient is 399 | * applied when mixing, and is modified to be: y / x. 400 | */ 401 | f[0] = d[0] + (this->Late.MixCoeff * ( d[1] + -d[2] + d[3])); 402 | f[1] = d[1] + (this->Late.MixCoeff * (-d[0] + d[2] + d[3])); 403 | f[2] = d[2] + (this->Late.MixCoeff * ( d[0] + -d[1] + d[3])); 404 | f[3] = d[3] + (this->Late.MixCoeff * (-d[0] + -d[1] + -d[2] )); 405 | 406 | // Output the results of the matrix for all four channels, attenuated by 407 | // the late reverb gain (which is attenuated by the 'x' mix coefficient). 408 | out[0] = this->Late.Gain * f[0]; 409 | out[1] = this->Late.Gain * f[1]; 410 | out[2] = this->Late.Gain * f[2]; 411 | out[3] = this->Late.Gain * f[3]; 412 | 413 | // Re-feed the cyclical delay lines. 414 | DelayLineIn(&this->Late.Delay[0], this->Offset, f[0]); 415 | DelayLineIn(&this->Late.Delay[1], this->Offset, f[1]); 416 | DelayLineIn(&this->Late.Delay[2], this->Offset, f[2]); 417 | DelayLineIn(&this->Late.Delay[3], this->Offset, f[3]); 418 | } 419 | 420 | // Given an input sample, this function mixes echo into the four-channel late 421 | // reverb. 422 | inline void ReverbEffect::EAXEcho(float in, float *late) 423 | { 424 | float out, feed; 425 | 426 | // Get the latest attenuated echo sample for output. 427 | feed = AttenuatedDelayLineOut(&this->Echo.Delay, 428 | this->Offset - this->Echo.Offset, 429 | this->Echo.Coeff); 430 | 431 | // Mix the output into the late reverb channels. 432 | out = this->Echo.MixCoeff[0] * feed; 433 | late[0] = (this->Echo.MixCoeff[1] * late[0]) + out; 434 | late[1] = (this->Echo.MixCoeff[1] * late[1]) + out; 435 | late[2] = (this->Echo.MixCoeff[1] * late[2]) + out; 436 | late[3] = (this->Echo.MixCoeff[1] * late[3]) + out; 437 | 438 | // Mix the energy-attenuated input with the output and pass it through 439 | // the echo low-pass filter. 440 | feed += this->Echo.DensityGain * in; 441 | feed = lerp(feed, this->Echo.LpSample, this->Echo.LpCoeff); 442 | this->Echo.LpSample = feed; 443 | 444 | // Then the echo all-pass filter. 445 | feed = AllpassInOut(&this->Echo.ApDelay, 446 | this->Offset - this->Echo.ApOffset, 447 | this->Offset, feed, this->Echo.ApFeedCoeff, 448 | this->Echo.ApCoeff); 449 | 450 | // Feed the delay with the mixed and filtered sample. 451 | DelayLineIn(&this->Echo.Delay, this->Offset, feed); 452 | } 453 | 454 | 455 | // Perform the EAX reverb pass on a given input sample, resulting in four-channel output. 456 | inline void ReverbEffect::EAXVerbPass(float in, float *early, float *late) 457 | { 458 | float feed, taps[4]; 459 | 460 | // Low-pass filter the incoming sample. 461 | in = FilterState_Process(&this->LpFilter, in); 462 | in = FilterState_Process(&this->HpFilter, in); 463 | 464 | // Perform any modulation on the input. 465 | in = this->EAXModulation(in); 466 | 467 | // Feed the initial delay line. 468 | DelayLineIn(&this->Delay, this->Offset, in); 469 | 470 | // Calculate the early reflection from the first delay tap. 471 | in = DelayLineOut(&this->Delay, this->Offset - this->DelayTap[0]); 472 | this->EarlyReflection(in, early); 473 | 474 | // Feed the decorrelator from the energy-attenuated output of the second 475 | // delay tap. 476 | in = DelayLineOut(&this->Delay, this->Offset - this->DelayTap[1]); 477 | feed = in * this->Late.DensityGain; 478 | DelayLineIn(&this->Decorrelator, this->Offset, feed); 479 | 480 | // Calculate the late reverb from the decorrelator taps. 481 | taps[0] = feed; 482 | taps[1] = DelayLineOut(&this->Decorrelator, this->Offset - this->DecoTap[0]); 483 | taps[2] = DelayLineOut(&this->Decorrelator, this->Offset - this->DecoTap[1]); 484 | taps[3] = DelayLineOut(&this->Decorrelator, this->Offset - this->DecoTap[2]); 485 | this->LateReverb(taps, late); 486 | 487 | // Calculate and mix in any echo. 488 | this->EAXEcho(in, late); 489 | 490 | // Step all delays forward one sample. 491 | this->Offset++; 492 | } 493 | 494 | void ReverbEffect::Process(uint32_t SamplesToDo, const float *SamplesIn, float *SamplesOut) 495 | { 496 | float (*early)[4] = this->EarlySamples; 497 | float (*late)[4] = this->ReverbSamples; 498 | uint32_t index, c; 499 | 500 | /* Process reverb for these samples. */ 501 | for(index = 0;index < SamplesToDo;index++) 502 | this->EAXVerbPass(SamplesIn[index], early[index], late[index]); 503 | 504 | for(c = 0;c < OUTPUT_CHANNELS ; c++) 505 | { 506 | float earlyGain, lateGain; 507 | 508 | earlyGain = this->Early.PanGain[c]; 509 | if(fabsf(earlyGain) > GAIN_SILENCE_THRESHOLD) 510 | { 511 | for(index = 0;index < SamplesToDo;index++) 512 | SamplesOut[index * 2 + c] = earlyGain*early[index][c&3]; 513 | } 514 | 515 | 516 | lateGain = this->Late.PanGain[c]; 517 | if(fabsf(lateGain) > GAIN_SILENCE_THRESHOLD) 518 | { 519 | for(index = 0;index < SamplesToDo;index++) 520 | SamplesOut[index * 2 + c] = lateGain*late[index][c&3]; 521 | } 522 | } 523 | } 524 | 525 | // Given the allocated sample buffer, this function updates each delay line offset. 526 | static inline void RealizeLineOffset(float *sampleBuffer, DelayLine *Delay) 527 | { 528 | Delay->Line = &sampleBuffer[(ptrdiff_t)Delay->Line]; 529 | } 530 | 531 | // Calculate the length of a delay line and store its mask and offset. 532 | static uint32_t CalcLineLength(float length, ptrdiff_t offset, uint32_t frequency, DelayLine *Delay) 533 | { 534 | uint32_t samples; 535 | 536 | // All line lengths are powers of 2, calculated from their lengths, with 537 | // an additional sample in case of rounding errors. 538 | samples = NextPowerOf2(Truncate(length * frequency) + 1); 539 | // All lines share a single sample buffer. 540 | Delay->Mask = samples - 1; 541 | Delay->Line = (float*)offset; 542 | // Return the sample count for accumulation. 543 | return samples; 544 | } 545 | 546 | // Calculates the delay line metrics and allocates the shared sample buffer for all lines given the sample rate (frequency). 547 | void ReverbEffect::AllocLines(uint32_t frequency) 548 | { 549 | uint32_t totalSamples, index; 550 | float length; 551 | 552 | // All delay line lengths are calculated to accomodate the full range of lengths given their respective paramters. 553 | totalSamples = 0; 554 | 555 | /* The modulator's line length is calculated from the maximum modulation 556 | * time and depth coefficient, and halfed for the low-to-high frequency 557 | * swing. An additional sample is added to keep it stable when there is no modulation. 558 | */ 559 | length = (EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f) + (1.0f / frequency); 560 | totalSamples += CalcLineLength(length, totalSamples, frequency, &this->Mod.Delay); 561 | 562 | // The initial delay is the sum of the reflections and late reverb delays. 563 | length = EAXREVERB_MAX_REFLECTIONS_DELAY + EAXREVERB_MAX_LATE_REVERB_DELAY; 564 | totalSamples += CalcLineLength(length, totalSamples, frequency, &this->Delay); 565 | 566 | // The early reflection lines. 567 | for(index = 0;index < 4;index++) 568 | totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples, frequency, &this->Early.Delay[index]); 569 | 570 | // The decorrelator line is calculated from the lowest reverb density (a 571 | // parameter value of 1). 572 | length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) * LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER); 573 | totalSamples += CalcLineLength(length, totalSamples, frequency, &this->Decorrelator); 574 | 575 | // The late all-pass lines. 576 | for(index = 0;index < 4;index++) 577 | totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples, frequency, &this->Late.ApDelay[index]); 578 | 579 | // The late delay lines are calculated from the lowest reverb density. 580 | for(index = 0;index < 4;index++) 581 | { 582 | length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER); 583 | totalSamples += CalcLineLength(length, totalSamples, frequency, &this->Late.Delay[index]); 584 | } 585 | 586 | // The echo all-pass and delay lines. 587 | totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples, frequency, &this->Echo.ApDelay); 588 | totalSamples += CalcLineLength(EAXREVERB_MAX_ECHO_TIME, totalSamples, frequency, &this->Echo.Delay); 589 | 590 | float* newBuf = new float[totalSamples]; 591 | this->SampleBuffer = newBuf; 592 | this->TotalSamples = totalSamples; 593 | 594 | // Update all delays to reflect the new sample buffer. 595 | RealizeLineOffset(this->SampleBuffer, &this->Delay); 596 | RealizeLineOffset(this->SampleBuffer, &this->Decorrelator); 597 | for(index = 0;index < 4;index++) 598 | { 599 | RealizeLineOffset(this->SampleBuffer, &this->Early.Delay[index]); 600 | RealizeLineOffset(this->SampleBuffer, &this->Late.ApDelay[index]); 601 | RealizeLineOffset(this->SampleBuffer, &this->Late.Delay[index]); 602 | } 603 | RealizeLineOffset(this->SampleBuffer, &this->Mod.Delay); 604 | RealizeLineOffset(this->SampleBuffer, &this->Echo.ApDelay); 605 | RealizeLineOffset(this->SampleBuffer, &this->Echo.Delay); 606 | 607 | // Clear the sample buffer. 608 | for(index = 0;index < this->TotalSamples;index++) 609 | this->SampleBuffer[index] = 0.0f; 610 | } 611 | 612 | // Calculate a decay coefficient given the length of each cycle and the time until the decay reaches -60 dB. 613 | static inline float CalcDecayCoeff(float length, float decayTime) 614 | { 615 | return powf(0.001f/*-60 dB*/, length/decayTime); 616 | } 617 | 618 | // Calculate a decay length from a coefficient and the time until the decay reaches -60 dB. 619 | static inline float CalcDecayLength(float coeff, float decayTime) 620 | { 621 | return log10f(coeff) * decayTime / log10f(0.001f)/*-60 dB*/; 622 | } 623 | 624 | // Calculate an attenuation to be applied to the input of any echo models to 625 | // compensate for modal density and decay time. 626 | static inline float CalcDensityGain(float a) 627 | { 628 | /* The energy of a signal can be obtained by finding the area under the 629 | * squared signal. This takes the form of Sum(x_n^2), where x is the 630 | * amplitude for the sample n. 631 | * 632 | * Decaying feedback matches exponential decay of the form Sum(a^n), 633 | * where a is the attenuation coefficient, and n is the sample. The area 634 | * under this decay curve can be calculated as: 1 / (1 - a). 635 | * 636 | * Modifying the above equation to find the squared area under the curve 637 | * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be 638 | * calculated by inverting the square root of this approximation, 639 | * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2). 640 | */ 641 | return sqrtf(1.0f - (a * a)); 642 | } 643 | 644 | // Calculate the mixing matrix coefficients given a diffusion factor. 645 | static inline void CalcMatrixCoeffs(float diffusion, float *x, float *y) 646 | { 647 | float n, t; 648 | 649 | // The matrix is of order 4, so n is sqrt (4 - 1). 650 | n = sqrtf(3.0f); 651 | t = diffusion * atanf(n); 652 | 653 | // Calculate the first mixing matrix coefficient. 654 | *x = cosf(t); 655 | // Calculate the second mixing matrix coefficient. 656 | *y = sinf(t) / n; 657 | } 658 | 659 | // Calculate the limited HF ratio for use with the late reverb low-pass filters. 660 | static float CalcLimitedHfRatio(float hfRatio, float airAbsorptionGainHF, float decayTime) 661 | { 662 | float limitRatio; 663 | 664 | /* Find the attenuation due to air absorption in dB (converting delay 665 | * time to meters using the speed of sound). Then reversing the decay 666 | * equation, solve for HF ratio. The delay length is cancelled out of 667 | * the equation, so it can be calculated once for all lines. 668 | */ 669 | limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SPEEDOFSOUNDMETRESPERSEC); 670 | /* Using the limit calculated above, apply the upper bound to the HF 671 | * ratio. Also need to limit the result to a minimum of 0.1, just like the 672 | * HF ratio parameter. */ 673 | return clampf(limitRatio, 0.1f, hfRatio); 674 | } 675 | 676 | // Calculate the coefficient for a HF (and eventually LF) decay damping filter. 677 | static inline float CalcDampingCoeff(float hfRatio, float length, float decayTime, float decayCoeff, float cw) 678 | { 679 | float coeff, g; 680 | 681 | // Eventually this should boost the high frequencies when the ratio 682 | // exceeds 1. 683 | coeff = 0.0f; 684 | if (hfRatio < 1.0f) 685 | { 686 | // Calculate the low-pass coefficient by dividing the HF decay 687 | // coefficient by the full decay coefficient. 688 | g = CalcDecayCoeff(length, decayTime * hfRatio) / decayCoeff; 689 | 690 | // Damping is done with a 1-pole filter, so g needs to be squared. 691 | g *= g; 692 | if(g < 0.9999f) /* 1-epsilon */ 693 | { 694 | /* Be careful with gains < 0.001, as that causes the coefficient 695 | * head towards 1, which will flatten the signal. */ 696 | g = maxf(g, 0.001f); 697 | coeff = (1 - g*cw - sqrtf(2*g*(1-cw) - g*g*(1 - cw*cw))) / 698 | (1 - g); 699 | } 700 | 701 | // Very low decay times will produce minimal output, so apply an 702 | // upper bound to the coefficient. 703 | coeff = minf(coeff, 0.98f); 704 | } 705 | return coeff; 706 | } 707 | 708 | // Update the EAX modulation index, range, and depth. 709 | // Keep in mind that this kind of vibrato is additive and not multiplicative as one may expect. 710 | // The downswing will sound stronger than the upswing. 711 | void ReverbEffect::UpdateModulator(float modTime, float modDepth, uint32_t frequency) 712 | { 713 | uint32_t range; 714 | 715 | /* Modulation is calculated in two parts. 716 | * 717 | * The modulation time effects the sinus applied to the change in 718 | * frequency. An index out of the current time range (both in samples) 719 | * is incremented each sample. The range is bound to a reasonable 720 | * minimum (1 sample) and when the timing changes, the index is rescaled 721 | * to the new range (to keep the sinus consistent). 722 | */ 723 | range = maxu(Truncate(modTime*frequency), 1); 724 | this->Mod.Index = (uint32_t)(this->Mod.Index * (uint64_t)range / this->Mod.Range); 725 | this->Mod.Range = range; 726 | 727 | /* The modulation depth effects the amount of frequency change over the 728 | * range of the sinus. It needs to be scaled by the modulation time so 729 | * that a given depth produces a consistent change in frequency over all 730 | * ranges of time. Since the depth is applied to a sinus value, it needs 731 | * to be halfed once for the sinus range and again for the sinus swing 732 | * in time (half of it is spent decreasing the frequency, half is spent 733 | * increasing it). 734 | */ 735 | this->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f / 736 | 2.0f * frequency; 737 | } 738 | 739 | // Update the offsets for the initial effect delay line. 740 | void ReverbEffect::UpdateDelayLine(float earlyDelay, float lateDelay, uint32_t frequency) 741 | { 742 | // Calculate the initial delay taps. 743 | this->DelayTap[0] = Truncate(earlyDelay * frequency); 744 | this->DelayTap[1] = Truncate((earlyDelay + lateDelay) * frequency); 745 | } 746 | 747 | // Update the early reflections gain and line coefficients. 748 | void ReverbEffect::UpdateEarlyLines(float reverbGain, float earlyGain, float lateDelay) 749 | { 750 | uint32_t index; 751 | 752 | // Calculate the early reflections gain (from the master effect gain, and 753 | // reflections gain parameters) with a constant attenuation of 0.5. 754 | this->Early.Gain = 0.5f * reverbGain * earlyGain; 755 | 756 | // Calculate the gain (coefficient) for each early delay line using the 757 | // late delay time. This expands the early reflections to the start of 758 | // the late reverb. 759 | for(index = 0;index < 4;index++) 760 | this->Early.Coeff[index] = CalcDecayCoeff(EARLY_LINE_LENGTH[index], 761 | lateDelay); 762 | } 763 | 764 | // Update the offsets for the decorrelator line. 765 | void ReverbEffect::UpdateDecorrelator(float density, uint32_t frequency) 766 | { 767 | uint32_t index; 768 | float length; 769 | 770 | /* The late reverb inputs are decorrelated to smooth the reverb tail and 771 | * reduce harsh echos. The first tap occurs immediately, while the 772 | * remaining taps are delayed by multiples of a fraction of the smallest 773 | * cyclical delay time. 774 | * 775 | * offset[index] = (FRACTION (MULTIPLIER^index)) smallest_delay 776 | */ 777 | for(index = 0;index < 3;index++) 778 | { 779 | length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (float)index)) * 780 | LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER)); 781 | this->DecoTap[index] = Truncate(length * frequency); 782 | } 783 | } 784 | 785 | // Update the late reverb gains, line lengths, and line coefficients. 786 | void ReverbEffect::UpdateLateLines(float reverbGain, float lateGain, float xMix, float density, float decayTime, float diffusion, float hfRatio, float cw, uint32_t frequency) 787 | { 788 | float length; 789 | uint32_t index; 790 | 791 | /* Calculate the late reverb gain (from the master effect gain, and late 792 | * reverb gain parameters). Since the output is tapped prior to the 793 | * application of the next delay line coefficients, this gain needs to be 794 | * attenuated by the 'x' mixing matrix coefficient as well. 795 | */ 796 | this->Late.Gain = reverbGain * lateGain * xMix; 797 | 798 | /* To compensate for changes in modal density and decay time of the late 799 | * reverb signal, the input is attenuated based on the maximal energy of 800 | * the outgoing signal. This approximation is used to keep the apparent 801 | * energy of the signal equal for all ranges of density and decay time. 802 | * 803 | * The average length of the cyclcical delay lines is used to calculate 804 | * the attenuation coefficient. 805 | */ 806 | length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] + 807 | LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]) / 4.0f; 808 | length *= 1.0f + (density * LATE_LINE_MULTIPLIER); 809 | this->Late.DensityGain = CalcDensityGain(CalcDecayCoeff(length, 810 | decayTime)); 811 | 812 | // Calculate the all-pass feed-back and feed-forward coefficient. 813 | this->Late.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f); 814 | 815 | for(index = 0;index < 4;index++) 816 | { 817 | // Calculate the gain (coefficient) for each all-pass line. 818 | this->Late.ApCoeff[index] = CalcDecayCoeff(ALLPASS_LINE_LENGTH[index], 819 | decayTime); 820 | 821 | // Calculate the length (in seconds) of each cyclical delay line. 822 | length = LATE_LINE_LENGTH[index] * (1.0f + (density * 823 | LATE_LINE_MULTIPLIER)); 824 | 825 | // Calculate the delay offset for each cyclical delay line. 826 | this->Late.Offset[index] = Truncate(length * frequency); 827 | 828 | // Calculate the gain (coefficient) for each cyclical line. 829 | this->Late.Coeff[index] = CalcDecayCoeff(length, decayTime); 830 | 831 | // Calculate the damping coefficient for each low-pass filter. 832 | this->Late.LpCoeff[index] = 833 | CalcDampingCoeff(hfRatio, length, decayTime, 834 | this->Late.Coeff[index], cw); 835 | 836 | // Attenuate the cyclical line coefficients by the mixing coefficient 837 | // (x). 838 | this->Late.Coeff[index] *= xMix; 839 | } 840 | } 841 | 842 | // Update the echo gain, line offset, line coefficients, and mixing coefficients. 843 | void ReverbEffect::UpdateEchoLine(float reverbGain, float lateGain, float echoTime, float decayTime, float diffusion, float echoDepth, float hfRatio, float cw, uint32_t frequency) 844 | { 845 | // Update the offset and coefficient for the echo delay line. 846 | this->Echo.Offset = Truncate(echoTime * frequency); 847 | 848 | // Calculate the decay coefficient for the echo line. 849 | this->Echo.Coeff = CalcDecayCoeff(echoTime, decayTime); 850 | 851 | // Calculate the energy-based attenuation coefficient for the echo delay 852 | // line. 853 | this->Echo.DensityGain = CalcDensityGain(this->Echo.Coeff); 854 | 855 | // Calculate the echo all-pass feed coefficient. 856 | this->Echo.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f); 857 | 858 | // Calculate the echo all-pass attenuation coefficient. 859 | this->Echo.ApCoeff = CalcDecayCoeff(ECHO_ALLPASS_LENGTH, decayTime); 860 | 861 | // Calculate the damping coefficient for each low-pass filter. 862 | this->Echo.LpCoeff = CalcDampingCoeff(hfRatio, echoTime, decayTime, 863 | this->Echo.Coeff, cw); 864 | 865 | /* Calculate the echo mixing coefficients. The first is applied to the 866 | * echo itself. The second is used to attenuate the late reverb when 867 | * echo depth is high and diffusion is low, so the echo is slightly 868 | * stronger than the decorrelated echos in the reverb tail. 869 | */ 870 | this->Echo.MixCoeff[0] = reverbGain * lateGain * echoDepth; 871 | this->Echo.MixCoeff[1] = 1.0f - (echoDepth * 0.5f * (1.0f - diffusion)); 872 | } 873 | 874 | // Update the early and late 3D panning gains. 875 | void ReverbEffect::Update3DPanning(const float *ReflectionsPan, const float *LateReverbPan, float Gain) 876 | { 877 | float earlyPan[3] = { ReflectionsPan[0], ReflectionsPan[1], -ReflectionsPan[2] }; 878 | float latePan[3] = { LateReverbPan[0], LateReverbPan[1], -LateReverbPan[2] }; 879 | float AmbientGains[OUTPUT_CHANNELS]; 880 | float DirGains[OUTPUT_CHANNELS]; 881 | float length, invlen; 882 | uint32_t i; 883 | 884 | ComputeAmbientGains(1.4142f, AmbientGains); 885 | 886 | length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2]; 887 | if(!(length > FLT_EPSILON)) 888 | { 889 | for(i = 0;i < OUTPUT_CHANNELS;i++) 890 | this->Early.PanGain[i] = AmbientGains[i] * Gain; 891 | } 892 | else 893 | { 894 | invlen = 1.0f / sqrtf(length); 895 | earlyPan[0] *= invlen; 896 | earlyPan[1] *= invlen; 897 | earlyPan[2] *= invlen; 898 | 899 | length = minf(length, 1.0f); 900 | ComputeDirectionalGains(earlyPan, 1.4142f, DirGains); 901 | for(i = 0;i < OUTPUT_CHANNELS;i++) 902 | this->Early.PanGain[i] = lerp(AmbientGains[i], DirGains[i], length) * Gain; 903 | } 904 | 905 | length = latePan[0]*latePan[0] + latePan[1]*latePan[1] + latePan[2]*latePan[2]; 906 | if(!(length > FLT_EPSILON)) 907 | { 908 | for(i = 0;i < OUTPUT_CHANNELS;i++) 909 | this->Late.PanGain[i] = AmbientGains[i] * Gain; 910 | } 911 | else 912 | { 913 | invlen = 1.0f / sqrtf(length); 914 | latePan[0] *= invlen; 915 | latePan[1] *= invlen; 916 | latePan[2] *= invlen; 917 | 918 | length = minf(length, 1.0f); 919 | ComputeDirectionalGains(latePan, 1.4142f, DirGains); 920 | for(i = 0;i < OUTPUT_CHANNELS;i++) 921 | this->Late.PanGain[i] = lerp(AmbientGains[i], DirGains[i], length) * Gain; 922 | } 923 | } 924 | 925 | 926 | void ReverbEffect::Update(int frequency) 927 | { 928 | float lfscale, hfscale, hfRatio; 929 | float cw, x, y; 930 | 931 | // Calculate the master low-pass filter (from the master effect HF gain). 932 | hfscale = this->settings.HFReference / frequency; 933 | FilterState_setParams(&this->LpFilter, Filter_HighShelf, this->settings.GainHF, hfscale, 0.0f); 934 | 935 | lfscale = this->settings.LFReference / frequency; 936 | FilterState_setParams(&this->HpFilter, Filter_LowShelf, this->settings.GainLF, lfscale, 0.0f); 937 | 938 | // Update the modulator line. 939 | this->UpdateModulator(this->settings.ModulationTime, this->settings.ModulationDepth, frequency); 940 | 941 | // Update the initial effect delay. 942 | this->UpdateDelayLine(this->settings.ReflectionsDelay, this->settings.LateReverbDelay, frequency); 943 | 944 | // Update the early lines. 945 | this->UpdateEarlyLines(this->settings.Gain, this->settings.ReflectionsGain, this->settings.LateReverbDelay); 946 | 947 | // Update the decorrelator. 948 | this->UpdateDecorrelator(this->settings.Density, frequency); 949 | 950 | // Get the mixing matrix coefficients (x and y). 951 | CalcMatrixCoeffs(this->settings.Diffusion, &x, &y); 952 | // Then divide x into y to simplify the matrix calculation. 953 | this->Late.MixCoeff = y / x; 954 | 955 | // If the HF limit parameter is flagged, calculate an appropriate limit 956 | // based on the air absorption parameter. 957 | hfRatio = this->settings.DecayHFRatio; 958 | 959 | if(this->settings.DecayHFLimit && this->settings.AirAbsorptionGainHF < 1.0f) 960 | hfRatio = CalcLimitedHfRatio(hfRatio, this->settings.AirAbsorptionGainHF, this->settings.DecayTime); 961 | 962 | cw = cosf(F_2PI * hfscale); 963 | // Update the late lines. 964 | this->UpdateLateLines(this->settings.Gain, this->settings.LateReverbGain, x, this->settings.Density, this->settings.DecayTime, this->settings.Diffusion, hfRatio, cw, frequency); 965 | 966 | // Update the echo line. 967 | this->UpdateEchoLine(this->settings.Gain, this->settings.LateReverbGain, this->settings.EchoTime, this->settings.DecayTime, this->settings.Diffusion, this->settings.EchoDepth, hfRatio, cw, frequency); 968 | 969 | // Update early and late 3D panning. 970 | this->Update3DPanning(this->settings.ReflectionsPan, this->settings.LateReverbPan, ReverbBoost); 971 | } 972 | 973 | 974 | float eaxDbToAmp(float eaxDb){ 975 | float dB = eaxDb / 2000.0f; 976 | return pow(10.0f, dB); 977 | } 978 | 979 | void ReverbEffect::LoadPreset(int environment, float environmentSize, float environmentDiffusion, int room, int roomHF, int roomLF, 980 | float decayTime, float decayHFRatio, float decayLFRatio, 981 | int reflections, float reflectionsDelay, float reflectionsPanX, float reflectionsPanY, float reflectionsPanZ, 982 | int reverb, float reverbDelay, float reverbPanX, float reverbPanY, float reverbPanZ, 983 | float echoTime, float echoDepth, float modulationTime, float modulationDepth, float airAbsorptionHF, 984 | float hfReference, float lfReference, float roomRolloffFactor, int flags) 985 | { 986 | this->settings.Density = 1.0f; // todo, currently default 987 | this->settings.Diffusion = environmentDiffusion; 988 | this->settings.Gain = eaxDbToAmp(room); //0.32f; 989 | this->settings.GainHF = eaxDbToAmp(roomHF); //0.89f; 990 | this->settings.GainLF = eaxDbToAmp(roomLF); // 1.0f; 991 | this->settings.DecayTime = decayTime; 992 | this->settings.DecayHFRatio = decayHFRatio; 993 | this->settings.DecayLFRatio = decayLFRatio; 994 | this->settings.ReflectionsGain = eaxDbToAmp(reflections); // 0.05f; 995 | this->settings.ReflectionsDelay = reflectionsDelay; 996 | this->settings.ReflectionsPan[0] = reflectionsPanX; 997 | this->settings.ReflectionsPan[1] = reflectionsPanY; 998 | this->settings.ReflectionsPan[2] = reflectionsPanZ; 999 | this->settings.LateReverbGain = eaxDbToAmp(reverb); //1.26f; 1000 | this->settings.LateReverbDelay = reverbDelay; 1001 | this->settings.LateReverbPan[0] = reverbPanX; 1002 | this->settings.LateReverbPan[1] = reverbPanY; 1003 | this->settings.LateReverbPan[2] = reverbPanZ; 1004 | this->settings.EchoTime = echoTime; 1005 | this->settings.EchoDepth = echoDepth; 1006 | this->settings.ModulationTime = modulationTime; 1007 | this->settings.ModulationDepth = modulationDepth; 1008 | this->settings.AirAbsorptionGainHF = eaxDbToAmp(airAbsorptionHF); //0.995f; 1009 | this->settings.HFReference = hfReference; 1010 | this->settings.LFReference = lfReference; 1011 | this->settings.RoomRolloffFactor = roomRolloffFactor; 1012 | this->settings.DecayHFLimit = 1; 1013 | } 1014 | 1015 | ReverbEffect::ReverbEffect(uint32_t frequency) 1016 | { 1017 | ambiCoeffs[0][0] = 0.7071f; 1018 | ambiCoeffs[0][1] = 0.5f; 1019 | ambiCoeffs[0][2] = 0.0f; 1020 | ambiCoeffs[0][3] = 0.0f; 1021 | 1022 | ambiCoeffs[1][0] = 0.7071f; 1023 | ambiCoeffs[1][1] = -0.5f; 1024 | ambiCoeffs[2][2] = 0.0f; 1025 | ambiCoeffs[3][3] = 0.0f; 1026 | 1027 | uint32_t index; 1028 | 1029 | this->TotalSamples = 0; 1030 | this->SampleBuffer = NULL; 1031 | 1032 | FilterState_clear(&this->LpFilter); 1033 | FilterState_clear(&this->HpFilter); 1034 | 1035 | this->Mod.Delay.Mask = 0; 1036 | this->Mod.Delay.Line = NULL; 1037 | this->Mod.Index = 0; 1038 | this->Mod.Range = 1; 1039 | this->Mod.Depth = 0.0f; 1040 | this->Mod.Coeff = 0.0f; 1041 | this->Mod.Filter = 0.0f; 1042 | 1043 | this->Delay.Mask = 0; 1044 | this->Delay.Line = NULL; 1045 | this->DelayTap[0] = 0; 1046 | this->DelayTap[1] = 0; 1047 | 1048 | this->Early.Gain = 0.0f; 1049 | for(index = 0;index < 4;index++) 1050 | { 1051 | this->Early.Coeff[index] = 0.0f; 1052 | this->Early.Delay[index].Mask = 0; 1053 | this->Early.Delay[index].Line = NULL; 1054 | this->Early.Offset[index] = 0; 1055 | } 1056 | 1057 | this->Decorrelator.Mask = 0; 1058 | this->Decorrelator.Line = NULL; 1059 | this->DecoTap[0] = 0; 1060 | this->DecoTap[1] = 0; 1061 | this->DecoTap[2] = 0; 1062 | 1063 | this->Late.Gain = 0.0f; 1064 | this->Late.DensityGain = 0.0f; 1065 | this->Late.ApFeedCoeff = 0.0f; 1066 | this->Late.MixCoeff = 0.0f; 1067 | for(index = 0;index < 4;index++) 1068 | { 1069 | this->Late.ApCoeff[index] = 0.0f; 1070 | this->Late.ApDelay[index].Mask = 0; 1071 | this->Late.ApDelay[index].Line = NULL; 1072 | this->Late.ApOffset[index] = 0; 1073 | 1074 | this->Late.Coeff[index] = 0.0f; 1075 | this->Late.Delay[index].Mask = 0; 1076 | this->Late.Delay[index].Line = NULL; 1077 | this->Late.Offset[index] = 0; 1078 | 1079 | this->Late.LpCoeff[index] = 0.0f; 1080 | this->Late.LpSample[index] = 0.0f; 1081 | } 1082 | 1083 | for(index = 0;index < OUTPUT_CHANNELS;index++) 1084 | { 1085 | this->Early.PanGain[index] = 0.0f; 1086 | this->Late.PanGain[index] = 0.0f; 1087 | } 1088 | 1089 | this->Echo.DensityGain = 0.0f; 1090 | this->Echo.Delay.Mask = 0; 1091 | this->Echo.Delay.Line = NULL; 1092 | this->Echo.ApDelay.Mask = 0; 1093 | this->Echo.ApDelay.Line = NULL; 1094 | this->Echo.Coeff = 0.0f; 1095 | this->Echo.ApFeedCoeff = 0.0f; 1096 | this->Echo.ApCoeff = 0.0f; 1097 | this->Echo.Offset = 0; 1098 | this->Echo.ApOffset = 0; 1099 | this->Echo.LpCoeff = 0.0f; 1100 | this->Echo.LpSample = 0.0f; 1101 | this->Echo.MixCoeff[0] = 0.0f; 1102 | this->Echo.MixCoeff[1] = 0.0f; 1103 | 1104 | this->Offset = 0; 1105 | 1106 | this->settings.Density = EAXREVERB_DEFAULT_DENSITY; 1107 | this->settings.Diffusion = EAXREVERB_DEFAULT_DIFFUSION; 1108 | this->settings.Gain = EAXREVERB_DEFAULT_GAIN; 1109 | this->settings.GainHF = EAXREVERB_DEFAULT_GAINHF; 1110 | this->settings.GainLF = EAXREVERB_DEFAULT_GAINLF; 1111 | this->settings.DecayTime = EAXREVERB_DEFAULT_DECAY_TIME; 1112 | this->settings.DecayHFRatio = EAXREVERB_DEFAULT_DECAY_HFRATIO; 1113 | this->settings.DecayLFRatio = EAXREVERB_DEFAULT_DECAY_LFRATIO; 1114 | this->settings.ReflectionsGain = EAXREVERB_DEFAULT_REFLECTIONS_GAIN; 1115 | this->settings.ReflectionsDelay = EAXREVERB_DEFAULT_REFLECTIONS_DELAY; 1116 | this->settings.ReflectionsPan[0] = EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; 1117 | this->settings.ReflectionsPan[1] = EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; 1118 | this->settings.ReflectionsPan[2] = EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; 1119 | this->settings.LateReverbGain = EAXREVERB_DEFAULT_LATE_REVERB_GAIN; 1120 | this->settings.LateReverbDelay = EAXREVERB_DEFAULT_LATE_REVERB_DELAY; 1121 | this->settings.LateReverbPan[0] = EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; 1122 | this->settings.LateReverbPan[1] = EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; 1123 | this->settings.LateReverbPan[2] = EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; 1124 | this->settings.EchoTime = EAXREVERB_DEFAULT_ECHO_TIME; 1125 | this->settings.EchoDepth = EAXREVERB_DEFAULT_ECHO_DEPTH; 1126 | this->settings.ModulationTime = EAXREVERB_DEFAULT_MODULATION_TIME; 1127 | this->settings.ModulationDepth = EAXREVERB_DEFAULT_MODULATION_DEPTH; 1128 | this->settings.AirAbsorptionGainHF = EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; 1129 | this->settings.HFReference = EAXREVERB_DEFAULT_HFREFERENCE; 1130 | this->settings.LFReference = EAXREVERB_DEFAULT_LFREFERENCE; 1131 | this->settings.RoomRolloffFactor = EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; 1132 | this->settings.DecayHFLimit = EAXREVERB_DEFAULT_DECAY_HFLIMIT; 1133 | 1134 | // Allocate the delay lines. 1135 | AllocLines(frequency); 1136 | 1137 | // Calculate the modulation filter coefficient. Notice that the exponent 1138 | // is calculated given the current sample rate. This ensures that the 1139 | // resulting filter response over time is consistent across all sample 1140 | // rates. 1141 | this->Mod.Coeff = powf(MODULATION_FILTER_COEFF, 1142 | MODULATION_FILTER_CONST / frequency); 1143 | 1144 | // The early reflection and late all-pass filter line lengths are static, 1145 | // so their offsets only need to be calculated once. 1146 | for(index = 0;index < 4;index++) 1147 | { 1148 | this->Early.Offset[index] = Truncate(EARLY_LINE_LENGTH[index] * 1149 | frequency); 1150 | this->Late.ApOffset[index] = Truncate(ALLPASS_LINE_LENGTH[index] * 1151 | frequency); 1152 | } 1153 | 1154 | // The echo all-pass filter line length is static, so its offset only 1155 | // needs to be calculated once. 1156 | this->Echo.ApOffset = Truncate(ECHO_ALLPASS_LENGTH * frequency); 1157 | 1158 | } 1159 | 1160 | ReverbEffect::~ReverbEffect() 1161 | { 1162 | delete[] this->SampleBuffer; 1163 | this->SampleBuffer = NULL; 1164 | } 1165 | --------------------------------------------------------------------------------