diff options
Diffstat (limited to 'Alc/effects')
-rw-r--r-- | Alc/effects/autowah.c | 26 | ||||
-rw-r--r-- | Alc/effects/chorus.c | 26 | ||||
-rw-r--r-- | Alc/effects/compressor.c | 25 | ||||
-rw-r--r-- | Alc/effects/dedicated.c | 42 | ||||
-rw-r--r-- | Alc/effects/distortion.c | 38 | ||||
-rw-r--r-- | Alc/effects/echo.c | 34 | ||||
-rw-r--r-- | Alc/effects/equalizer.c | 53 | ||||
-rw-r--r-- | Alc/effects/flanger.c | 26 | ||||
-rw-r--r-- | Alc/effects/modulator.c | 33 | ||||
-rw-r--r-- | Alc/effects/null.c | 5 | ||||
-rw-r--r-- | Alc/effects/reverb.c | 826 |
11 files changed, 587 insertions, 547 deletions
diff --git a/Alc/effects/autowah.c b/Alc/effects/autowah.c index c8317c8b..6770f719 100644 --- a/Alc/effects/autowah.c +++ b/Alc/effects/autowah.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -31,15 +31,15 @@ /* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up * or down depending on the input signal, and a resonant peak at the cutoff. * - * Currently, we assume a cutoff frequency range of 500hz (no amplitude) to - * 3khz (peak gain). Peak gain is assumed to be in normalized scale. + * Currently, we assume a cutoff frequency range of 20hz (no amplitude) to + * 20khz (peak gain). Peak gain is assumed to be in normalized scale. */ typedef struct ALautowahState { DERIVE_FROM_TYPE(ALeffectState); /* Effect gains for each channel */ - ALfloat Gain[MaxChannels]; + ALfloat Gain[MAX_OUTPUT_CHANNELS]; /* Effect parameters */ ALfloat AttackRate; @@ -66,7 +66,6 @@ static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *d static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot) { ALfloat attackTime, releaseTime; - ALfloat gain; attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency; releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency; @@ -76,19 +75,18 @@ static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, co state->PeakGain = slot->EffectProps.Autowah.PeakGain; state->Resonance = slot->EffectProps.Autowah.Resonance; - gain = sqrtf(1.0f / device->NumChan) * slot->Gain; - SetGains(device, gain, state->Gain); + ComputeAmbientGains(device, slot->Gain, state->Gain); } -static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE]) +static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels) { ALuint it, kt; ALuint base; for(base = 0;base < SamplesToDo;) { - ALfloat temps[64]; - ALuint td = minu(SamplesToDo-base, 64); + ALfloat temps[256]; + ALuint td = minu(256, SamplesToDo-base); ALfloat gain = state->GainCtrl; for(it = 0;it < td;it++) @@ -114,7 +112,7 @@ static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, * ALfilterType_LowPass. However, instead of passing a bandwidth, * we use the resonance property for Q. This also inlines the call. */ - w0 = F_2PI * cutoff / state->Frequency; + w0 = F_TAU * cutoff / state->Frequency; /* FIXME: Resonance controls the resonant peak, or Q. How? Not sure * that Q = resonance*0.1. */ @@ -137,10 +135,10 @@ static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, } state->GainCtrl = gain; - for(kt = 0;kt < MaxChannels;kt++) + for(kt = 0;kt < NumChannels;kt++) { ALfloat gain = state->Gain[kt]; - if(!(gain > GAIN_SILENCE_THRESHOLD)) + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) continue; for(it = 0;it < td;it++) diff --git a/Alc/effects/chorus.c b/Alc/effects/chorus.c index a6bb199e..7aa5898b 100644 --- a/Alc/effects/chorus.c +++ b/Alc/effects/chorus.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -46,7 +46,7 @@ typedef struct ALchorusState { ALint lfo_disp; /* Gains for left and right sides */ - ALfloat Gain[2][MaxChannels]; + ALfloat Gain[2][MAX_OUTPUT_CHANNELS]; /* effect parameters */ enum ChorusWaveForm waveform; @@ -93,6 +93,8 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot) { + static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f }; + static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f }; ALfloat frequency = (ALfloat)Device->Frequency; ALfloat rate; ALint phase; @@ -111,8 +113,8 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency); /* Gains for left and right sides */ - ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]); - ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]); + ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]); + ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]); phase = Slot->EffectProps.Chorus.Phase; rate = Slot->EffectProps.Chorus.Rate; @@ -132,7 +134,7 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons state->lfo_scale = 4.0f / state->lfo_range; break; case CWF_Sinusoid: - state->lfo_scale = F_2PI / state->lfo_range; + state->lfo_scale = F_TAU / state->lfo_range; break; } @@ -201,15 +203,15 @@ DECL_TEMPLATE(Sinusoid) #undef DECL_TEMPLATE -static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { ALuint it, kt; ALuint base; for(base = 0;base < SamplesToDo;) { - ALfloat temps[64][2]; - ALuint td = minu(SamplesToDo-base, 64); + ALfloat temps[128][2]; + ALuint td = minu(128, SamplesToDo-base); switch(state->waveform) { @@ -221,17 +223,17 @@ static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, co break; } - for(kt = 0;kt < MaxChannels;kt++) + for(kt = 0;kt < NumChannels;kt++) { ALfloat gain = state->Gain[0][kt]; - if(gain > GAIN_SILENCE_THRESHOLD) + if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { for(it = 0;it < td;it++) SamplesOut[kt][it+base] += temps[it][0] * gain; } gain = state->Gain[1][kt]; - if(gain > GAIN_SILENCE_THRESHOLD) + if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { for(it = 0;it < td;it++) SamplesOut[kt][it+base] += temps[it][1] * gain; diff --git a/Alc/effects/compressor.c b/Alc/effects/compressor.c index 9728cabe..9859a085 100644 --- a/Alc/effects/compressor.c +++ b/Alc/effects/compressor.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -31,7 +31,7 @@ typedef struct ALcompressorState { DERIVE_FROM_TYPE(ALeffectState); /* Effect gains for each channel */ - ALfloat Gain[MaxChannels]; + ALfloat Gain[MAX_OUTPUT_CHANNELS]; /* Effect parameters */ ALboolean Enabled; @@ -55,25 +55,22 @@ static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdev return AL_TRUE; } -static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *Device, const ALeffectslot *Slot) +static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *device, const ALeffectslot *slot) { - ALfloat gain; + state->Enabled = slot->EffectProps.Compressor.OnOff; - state->Enabled = Slot->EffectProps.Compressor.OnOff; - - gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain; - SetGains(Device, gain, state->Gain); + ComputeAmbientGains(device, slot->Gain, state->Gain); } -static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE]) +static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels) { ALuint it, kt; ALuint base; for(base = 0;base < SamplesToDo;) { - ALfloat temps[64]; - ALuint td = minu(SamplesToDo-base, 64); + ALfloat temps[256]; + ALuint td = minu(256, SamplesToDo-base); if(state->Enabled) { @@ -119,10 +116,10 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint Samples } - for(kt = 0;kt < MaxChannels;kt++) + for(kt = 0;kt < NumChannels;kt++) { ALfloat gain = state->Gain[kt]; - if(!(gain > GAIN_SILENCE_THRESHOLD)) + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) continue; for(it = 0;it < td;it++) diff --git a/Alc/effects/dedicated.c b/Alc/effects/dedicated.c index 89af1c84..e09cc682 100644 --- a/Alc/effects/dedicated.c +++ b/Alc/effects/dedicated.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -32,7 +32,7 @@ typedef struct ALdedicatedState { DERIVE_FROM_TYPE(ALeffectState); - ALfloat gains[MaxChannels]; + ALfloat gains[MAX_OUTPUT_CHANNELS]; } ALdedicatedState; @@ -48,27 +48,41 @@ static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot) { ALfloat Gain; - ALsizei s; + ALuint i; + + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + state->gains[i] = 0.0f; Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain; - if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE) - ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains); - else if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) + if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) + { + int idx; + if((idx=GetChannelIdxByName(device, LFE)) != -1) + state->gains[idx] = Gain; + } + else if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE) { - for(s = 0;s < MaxChannels;s++) - state->gains[s] = 0.0f; - state->gains[LFE] = Gain; + int idx; + /* Dialog goes to the front-center speaker if it exists, otherwise it + * plays from the front-center location. */ + if((idx=GetChannelIdxByName(device, FrontCenter)) != -1) + state->gains[idx] = Gain; + else + { + static const ALfloat front_dir[3] = { 0.0f, 0.0f, -1.0f }; + ComputeDirectionalGains(device, front_dir, Gain, state->gains); + } } } -static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { const ALfloat *gains = state->gains; ALuint i, c; - for(c = 0;c < MaxChannels;c++) + for(c = 0;c < NumChannels;c++) { - if(!(gains[c] > GAIN_SILENCE_THRESHOLD)) + if(!(fabsf(gains[c]) > GAIN_SILENCE_THRESHOLD)) continue; for(i = 0;i < SamplesToDo;i++) @@ -94,7 +108,7 @@ ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(fa if(!state) return NULL; SET_VTABLE2(ALdedicatedState, ALeffectState, state); - for(s = 0;s < MaxChannels;s++) + for(s = 0;s < MAX_OUTPUT_CHANNELS;s++) state->gains[s] = 0.0f; return STATIC_CAST(ALeffectState, state); diff --git a/Alc/effects/distortion.c b/Alc/effects/distortion.c index b535655b..221cec39 100644 --- a/Alc/effects/distortion.c +++ b/Alc/effects/distortion.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -34,7 +34,7 @@ typedef struct ALdistortionState { DERIVE_FROM_TYPE(ALeffectState); /* Effect gains for each channel */ - ALfloat Gain[MaxChannels]; + ALfloat Gain[MAX_OUTPUT_CHANNELS]; /* Effect parameters */ ALfilterState lowpass; @@ -58,7 +58,6 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Devi ALfloat bandwidth; ALfloat cutoff; ALfloat edge; - ALfloat gain; /* Store distorted signal attenuation settings */ state->attenuation = Slot->EffectProps.Distortion.Gain; @@ -73,23 +72,23 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Devi /* Bandwidth value is constant in octaves */ bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f); ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f, - cutoff / (frequency*4.0f), bandwidth); + cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth) + ); /* Bandpass filter */ cutoff = Slot->EffectProps.Distortion.EQCenter; /* Convert bandwidth in Hz to octaves */ bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f); ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f, - cutoff / (frequency*4.0f), bandwidth); + cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth) + ); - gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain; - SetGains(Device, gain, state->Gain); + ComputeAmbientGains(Device, Slot->Gain, state->Gain); } -static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { const ALfloat fc = state->edge_coeff; - float oversample_buffer[64][4]; ALuint base; ALuint it; ALuint ot; @@ -97,8 +96,8 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint Samples for(base = 0;base < SamplesToDo;) { - ALfloat temps[64]; - ALuint td = minu(SamplesToDo-base, 64); + float oversample_buffer[64][4]; + ALuint td = minu(64, SamplesToDo-base); /* Perform 4x oversampling to avoid aliasing. */ /* Oversampling greatly improves distortion */ @@ -150,20 +149,19 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint Samples smp = ALfilterState_processSingle(&state->bandpass, smp); oversample_buffer[it][ot] = smp; } - - /* Fourth step, final, do attenuation and perform decimation, */ - /* store only one sample out of 4. */ - temps[it] = oversample_buffer[it][0] * state->attenuation; } - for(kt = 0;kt < MaxChannels;kt++) + for(kt = 0;kt < NumChannels;kt++) { - ALfloat gain = state->Gain[kt]; - if(!(gain > GAIN_SILENCE_THRESHOLD)) + /* Fourth step, final, do attenuation and perform decimation, + * store only one sample out of 4. + */ + ALfloat gain = state->Gain[kt] * state->attenuation; + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) continue; for(it = 0;it < td;it++) - SamplesOut[kt][base+it] += gain * temps[it]; + SamplesOut[kt][base+it] += gain * oversample_buffer[it][0]; } base += td; diff --git a/Alc/effects/echo.c b/Alc/effects/echo.c index 049ae9c7..f5a53c36 100644 --- a/Alc/effects/echo.c +++ b/Alc/effects/echo.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -43,7 +43,7 @@ typedef struct ALechoState { } Tap[2]; ALuint Offset; /* The panning gains for the two taps */ - ALfloat Gain[2][MaxChannels]; + ALfloat Gain[2][MAX_OUTPUT_CHANNELS]; ALfloat FeedGain; @@ -83,9 +83,9 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device) static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot) { + ALfloat pandir[3] = { 0.0f, 0.0f, 0.0f }; ALuint frequency = Device->Frequency; - ALfloat lrpan, gain; - ALfloat dirGain; + ALfloat gain, lrpan; state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1; state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency); @@ -95,21 +95,23 @@ static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const AL state->FeedGain = Slot->EffectProps.Echo.Feedback; + gain = minf(1.0f - Slot->EffectProps.Echo.Damping, 0.01f); ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf, - 1.0f - Slot->EffectProps.Echo.Damping, - LOWPASSFREQREF/frequency, 0.0f); + gain, LOWPASSFREQREF/frequency, + calc_rcpQ_from_slope(gain, 0.75f)); gain = Slot->Gain; - dirGain = fabsf(lrpan); /* First tap panning */ - ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]); + pandir[0] = -lrpan; + ComputeDirectionalGains(Device, pandir, gain, state->Gain[0]); /* Second tap panning */ - ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]); + pandir[0] = +lrpan; + ComputeDirectionalGains(Device, pandir, gain, state->Gain[1]); } -static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { const ALuint mask = state->BufferLength-1; const ALuint tap1 = state->Tap[0].delay; @@ -121,8 +123,8 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const for(base = 0;base < SamplesToDo;) { - ALfloat temps[64][2]; - ALuint td = minu(SamplesToDo-base, 64); + ALfloat temps[128][2]; + ALuint td = minu(128, SamplesToDo-base); for(i = 0;i < td;i++) { @@ -138,17 +140,17 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const offset++; } - for(k = 0;k < MaxChannels;k++) + for(k = 0;k < NumChannels;k++) { ALfloat gain = state->Gain[0][k]; - if(gain > GAIN_SILENCE_THRESHOLD) + if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { for(i = 0;i < td;i++) SamplesOut[k][i+base] += temps[i][0] * gain; } gain = state->Gain[1][k]; - if(gain > GAIN_SILENCE_THRESHOLD) + if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { for(i = 0;i < td;i++) SamplesOut[k][i+base] += temps[i][1] * gain; diff --git a/Alc/effects/equalizer.c b/Alc/effects/equalizer.c index cfe7c46c..244667ab 100644 --- a/Alc/effects/equalizer.c +++ b/Alc/effects/equalizer.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -75,7 +75,7 @@ typedef struct ALequalizerState { DERIVE_FROM_TYPE(ALeffectState); /* Effect gains for each channel */ - ALfloat Gain[MaxChannels]; + ALfloat Gain[MAX_OUTPUT_CHANNELS]; /* Effect parameters */ ALfilterState filter[4]; @@ -93,33 +93,40 @@ static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot) { ALfloat frequency = (ALfloat)device->Frequency; - ALfloat gain = sqrtf(1.0f / device->NumChan) * slot->Gain; + ALfloat gain, freq_mult; - SetGains(device, gain, state->Gain); + ComputeAmbientGains(device, slot->Gain, state->Gain); - /* Calculate coefficients for the each type of filter */ + /* Calculate coefficients for the each type of filter. Note that the shelf + * filters' gain is for the reference frequency, which is the centerpoint + * of the transition band. + */ + gain = sqrtf(slot->EffectProps.Equalizer.LowGain); + freq_mult = slot->EffectProps.Equalizer.LowCutoff/frequency; ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf, - sqrtf(slot->EffectProps.Equalizer.LowGain), - slot->EffectProps.Equalizer.LowCutoff/frequency, - 0.0f); + gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f) + ); + gain = slot->EffectProps.Equalizer.Mid1Gain; + freq_mult = slot->EffectProps.Equalizer.Mid1Center/frequency; ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking, - sqrtf(slot->EffectProps.Equalizer.Mid1Gain), - slot->EffectProps.Equalizer.Mid1Center/frequency, - slot->EffectProps.Equalizer.Mid1Width); + gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid1Width) + ); + gain = slot->EffectProps.Equalizer.Mid2Gain; + freq_mult = slot->EffectProps.Equalizer.Mid2Center/frequency; ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking, - sqrtf(slot->EffectProps.Equalizer.Mid2Gain), - slot->EffectProps.Equalizer.Mid2Center/frequency, - slot->EffectProps.Equalizer.Mid2Width); + gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid2Width) + ); + gain = sqrtf(slot->EffectProps.Equalizer.HighGain); + freq_mult = slot->EffectProps.Equalizer.HighCutoff/frequency; ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf, - sqrtf(slot->EffectProps.Equalizer.HighGain), - slot->EffectProps.Equalizer.HighCutoff/frequency, - 0.0f); + gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f) + ); } -static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { ALuint base; ALuint it; @@ -128,8 +135,8 @@ static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesTo for(base = 0;base < SamplesToDo;) { - ALfloat temps[64]; - ALuint td = minu(SamplesToDo-base, 64); + ALfloat temps[256]; + ALuint td = minu(256, SamplesToDo-base); for(it = 0;it < td;it++) { @@ -141,10 +148,10 @@ static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesTo temps[it] = smp; } - for(kt = 0;kt < MaxChannels;kt++) + for(kt = 0;kt < NumChannels;kt++) { ALfloat gain = state->Gain[kt]; - if(!(gain > GAIN_SILENCE_THRESHOLD)) + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) continue; for(it = 0;it < td;it++) diff --git a/Alc/effects/flanger.c b/Alc/effects/flanger.c index 117cd1f2..f6191abd 100644 --- a/Alc/effects/flanger.c +++ b/Alc/effects/flanger.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -46,7 +46,7 @@ typedef struct ALflangerState { ALint lfo_disp; /* Gains for left and right sides */ - ALfloat Gain[2][MaxChannels]; + ALfloat Gain[2][MAX_OUTPUT_CHANNELS]; /* effect parameters */ enum FlangerWaveForm waveform; @@ -93,6 +93,8 @@ static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *D static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot) { + static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f }; + static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f }; ALfloat frequency = (ALfloat)Device->Frequency; ALfloat rate; ALint phase; @@ -111,8 +113,8 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency); /* Gains for left and right sides */ - ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]); - ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]); + ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]); + ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]); phase = Slot->EffectProps.Flanger.Phase; rate = Slot->EffectProps.Flanger.Rate; @@ -132,7 +134,7 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co state->lfo_scale = 4.0f / state->lfo_range; break; case FWF_Sinusoid: - state->lfo_scale = F_2PI / state->lfo_range; + state->lfo_scale = F_TAU / state->lfo_range; break; } @@ -201,15 +203,15 @@ DECL_TEMPLATE(Sinusoid) #undef DECL_TEMPLATE -static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { ALuint it, kt; ALuint base; for(base = 0;base < SamplesToDo;) { - ALfloat temps[64][2]; - ALuint td = minu(SamplesToDo-base, 64); + ALfloat temps[128][2]; + ALuint td = minu(128, SamplesToDo-base); switch(state->waveform) { @@ -221,17 +223,17 @@ static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, break; } - for(kt = 0;kt < MaxChannels;kt++) + for(kt = 0;kt < NumChannels;kt++) { ALfloat gain = state->Gain[0][kt]; - if(gain > GAIN_SILENCE_THRESHOLD) + if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { for(it = 0;it < td;it++) SamplesOut[kt][it+base] += temps[it][0] * gain; } gain = state->Gain[1][kt]; - if(gain > GAIN_SILENCE_THRESHOLD) + if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { for(it = 0;it < td;it++) SamplesOut[kt][it+base] += temps[it][1] * gain; diff --git a/Alc/effects/modulator.c b/Alc/effects/modulator.c index a5475708..dceb408e 100644 --- a/Alc/effects/modulator.c +++ b/Alc/effects/modulator.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -42,7 +42,7 @@ typedef struct ALmodulatorState { ALuint index; ALuint step; - ALfloat Gain[MaxChannels]; + ALfloat Gain[MAX_OUTPUT_CHANNELS]; ALfilterState Filter; } ALmodulatorState; @@ -53,7 +53,7 @@ typedef struct ALmodulatorState { static inline ALfloat Sin(ALuint index) { - return sinf(index*(F_2PI/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f; + return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f; } static inline ALfloat Saw(ALuint index) @@ -69,7 +69,7 @@ static inline ALfloat Square(ALuint index) #define DECL_TEMPLATE(func) \ static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \ const ALfloat *restrict SamplesIn, \ - ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \ + ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) \ { \ const ALuint step = state->step; \ ALuint index = state->index; \ @@ -77,8 +77,8 @@ static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \ \ for(base = 0;base < SamplesToDo;) \ { \ - ALfloat temps[64]; \ - ALuint td = minu(SamplesToDo-base, 64); \ + ALfloat temps[256]; \ + ALuint td = minu(256, SamplesToDo-base); \ ALuint i, k; \ \ for(i = 0;i < td;i++) \ @@ -92,10 +92,10 @@ static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \ temps[i] = samp * func(index); \ } \ \ - for(k = 0;k < MaxChannels;k++) \ + for(k = 0;k < NumChannels;k++) \ { \ ALfloat gain = state->Gain[k]; \ - if(!(gain > GAIN_SILENCE_THRESHOLD)) \ + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) \ continue; \ \ for(i = 0;i < td;i++) \ @@ -125,7 +125,7 @@ static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot) { - ALfloat gain, cw, a; + ALfloat cw, a; if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID) state->Waveform = SINUSOID; @@ -139,7 +139,7 @@ static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device if(state->step == 0) state->step = 1; /* Custom filter coeffs, which match the old version instead of a low-shelf. */ - cw = cosf(F_2PI * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency); + cw = cosf(F_TAU * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency); a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f); state->Filter.b[0] = a; @@ -149,24 +149,23 @@ static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device state->Filter.a[1] = -a; state->Filter.a[2] = 0.0f; - gain = sqrtf(1.0f/Device->NumChan) * Slot->Gain; - SetGains(Device, gain, state->Gain); + ComputeAmbientGains(Device, Slot->Gain, state->Gain); } -static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { switch(state->Waveform) { case SINUSOID: - ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut); + ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels); break; case SAWTOOTH: - ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut); + ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels); break; case SQUARE: - ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut); + ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels); break; } } diff --git a/Alc/effects/null.c b/Alc/effects/null.c index 6dacd021..adc4ca81 100644 --- a/Alc/effects/null.c +++ b/Alc/effects/null.c @@ -41,11 +41,8 @@ static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(d * input to the output buffer. The result should be added to the output buffer, * not replace it. */ -static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloat (*restrict samplesOut)[BUFFERSIZE]) +static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALuint UNUSED(NumChannels)) { - /* NOTE: Couldn't use the UNUSED macro on samplesOut due to the way GCC's - * __attribute__ declaration interacts with the parenthesis. */ - (void)samplesOut; } /* This allocates memory to store the object, before it gets constructed. diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c index 245aed41..e1013309 100644 --- a/Alc/effects/reverb.c +++ b/Alc/effects/reverb.c @@ -13,8 +13,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -32,6 +32,10 @@ #include "alError.h" +/* This is the maximum number of samples processed for each inner loop + * iteration. */ +#define MAX_UPDATE_SAMPLES 256 + typedef struct DelayLine { // The delay lines use sample lengths that are powers of 2 to allow the @@ -86,7 +90,7 @@ typedef struct ALreverbState { // The gain for each output channel based on 3D panning (only for the // EAX path). - ALfloat PanGain[MaxChannels]; + ALfloat PanGain[4][MAX_OUTPUT_CHANNELS]; } Early; // Decorrelator delay line. @@ -125,7 +129,7 @@ typedef struct ALreverbState { // The gain for each output channel based on 3D panning (only for the // EAX path). - ALfloat PanGain[MaxChannels]; + ALfloat PanGain[4][MAX_OUTPUT_CHANNELS]; } Late; struct { @@ -148,8 +152,8 @@ typedef struct ALreverbState { ALfloat LpCoeff; ALfloat LpSample; - // Echo mixing coefficients. - ALfloat MixCoeff[2]; + // Echo mixing coefficient. + ALfloat MixCoeff; } Echo; // The current read offset for all delay lines. @@ -157,11 +161,11 @@ typedef struct ALreverbState { // The gain for each output channel (non-EAX path only; aliased from // Late.PanGain) - ALfloat *Gain; + ALfloat (*Gain)[MAX_OUTPUT_CHANNELS]; - /* Temporary storage used when processing, before deinterlacing. */ - ALfloat ReverbSamples[BUFFERSIZE][4]; - ALfloat EarlySamples[BUFFERSIZE][4]; + /* Temporary storage used when processing. */ + ALfloat ReverbSamples[MAX_UPDATE_SAMPLES][4]; + ALfloat EarlySamples[MAX_UPDATE_SAMPLES][4]; } ALreverbState; /* This is a user config option for modifying the overall output of the reverb @@ -234,39 +238,21 @@ static inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in) Delay->Line[offset&Delay->Mask] = in; } -// Attenuated delay line output routine. -static inline ALfloat AttenuatedDelayLineOut(DelayLine *Delay, ALuint offset, ALfloat coeff) -{ - return coeff * Delay->Line[offset&Delay->Mask]; -} - -// Basic attenuated all-pass input/output routine. -static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff) -{ - ALfloat out, feed; - - out = DelayLineOut(Delay, outOffset); - feed = feedCoeff * in; - DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in); - - // The time-based attenuation is only applied to the delay output to - // keep it from affecting the feed-back path (which is already controlled - // by the all-pass feed coefficient). - return (coeff * out) - feed; -} - // Given an input sample, this function produces modulation for the late // reverb. -static inline ALfloat EAXModulation(ALreverbState *State, ALfloat in) +static inline ALfloat EAXModulation(ALreverbState *State, ALuint offset, ALfloat in) { - ALfloat sinus, frac; - ALuint offset; + ALfloat sinus, frac, fdelay; ALfloat out0, out1; + ALuint delay; // Calculate the sinus rythm (dependent on modulation time and the // sampling rate). The center of the sinus is moved to reduce the delay // of the effect when the time or depth are low. - sinus = 1.0f - cosf(F_2PI * State->Mod.Index / State->Mod.Range); + sinus = 1.0f - cosf(F_TAU * State->Mod.Index / State->Mod.Range); + + // Step the modulation index forward, keeping it bound to its range. + State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range; // The depth determines the range over which to read the input samples // from, so it must be filtered to reduce the distortion caused by even @@ -275,95 +261,96 @@ static inline ALfloat EAXModulation(ALreverbState *State, ALfloat in) State->Mod.Coeff); // Calculate the read offset and fraction between it and the next sample. - frac = (1.0f + (State->Mod.Filter * sinus)); - offset = fastf2u(frac); - frac -= offset; + frac = modff(State->Mod.Filter*sinus + 1.0f, &fdelay); + delay = fastf2u(fdelay); // Get the two samples crossed by the offset, and feed the delay line // with the next input sample. - out0 = DelayLineOut(&State->Mod.Delay, State->Offset - offset); - out1 = DelayLineOut(&State->Mod.Delay, State->Offset - offset - 1); - DelayLineIn(&State->Mod.Delay, State->Offset, in); - - // Step the modulation index forward, keeping it bound to its range. - State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range; + out0 = DelayLineOut(&State->Mod.Delay, offset - delay); + out1 = DelayLineOut(&State->Mod.Delay, offset - delay - 1); + DelayLineIn(&State->Mod.Delay, offset, in); // The output is obtained by linearly interpolating the two samples that // were acquired above. return lerp(out0, out1, frac); } -// Delay line output routine for early reflections. -static inline ALfloat EarlyDelayLineOut(ALreverbState *State, ALuint index) +// Given some input sample, this function produces four-channel outputs for the +// early reflections. +static inline ALvoid EarlyReflection(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4]) { - return AttenuatedDelayLineOut(&State->Early.Delay[index], - State->Offset - State->Early.Offset[index], - State->Early.Coeff[index]); + ALfloat d[4], v, f[4]; + ALuint i; + + for(i = 0;i < todo;i++) + { + ALuint offset = State->Offset+i; + + // Obtain the decayed results of each early delay line. + d[0] = DelayLineOut(&State->Early.Delay[0], offset-State->Early.Offset[0]) * State->Early.Coeff[0]; + d[1] = DelayLineOut(&State->Early.Delay[1], offset-State->Early.Offset[1]) * State->Early.Coeff[1]; + d[2] = DelayLineOut(&State->Early.Delay[2], offset-State->Early.Offset[2]) * State->Early.Coeff[2]; + d[3] = DelayLineOut(&State->Early.Delay[3], offset-State->Early.Offset[3]) * State->Early.Coeff[3]; + + /* The following uses a lossless scattering junction from waveguide + * theory. It actually amounts to a householder mixing matrix, which + * will produce a maximally diffuse response, and means this can + * probably be considered a simple feed-back delay network (FDN). + * N + * --- + * \ + * v = 2/N / d_i + * --- + * i=1 + */ + v = (d[0] + d[1] + d[2] + d[3]) * 0.5f; + // The junction is loaded with the input here. + v += DelayLineOut(&State->Delay, offset-State->DelayTap[0]); + + // Calculate the feed values for the delay lines. + f[0] = v - d[0]; + f[1] = v - d[1]; + f[2] = v - d[2]; + f[3] = v - d[3]; + + // Re-feed the delay lines. + DelayLineIn(&State->Early.Delay[0], offset, f[0]); + DelayLineIn(&State->Early.Delay[1], offset, f[1]); + DelayLineIn(&State->Early.Delay[2], offset, f[2]); + DelayLineIn(&State->Early.Delay[3], offset, f[3]); + + // Output the results of the junction for all four channels. + out[i][0] = State->Early.Gain * f[0]; + out[i][1] = State->Early.Gain * f[1]; + out[i][2] = State->Early.Gain * f[2]; + out[i][3] = State->Early.Gain * f[3]; + } } -// Given an input sample, this function produces four-channel output for the -// early reflections. -static inline ALvoid EarlyReflection(ALreverbState *State, ALfloat in, ALfloat *restrict out) +// Basic attenuated all-pass input/output routine. +static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff) { - ALfloat d[4], v, f[4]; + ALfloat out, feed; - // Obtain the decayed results of each early delay line. - d[0] = EarlyDelayLineOut(State, 0); - d[1] = EarlyDelayLineOut(State, 1); - d[2] = EarlyDelayLineOut(State, 2); - d[3] = EarlyDelayLineOut(State, 3); - - /* The following uses a lossless scattering junction from waveguide - * theory. It actually amounts to a householder mixing matrix, which - * will produce a maximally diffuse response, and means this can probably - * be considered a simple feed-back delay network (FDN). - * N - * --- - * \ - * v = 2/N / d_i - * --- - * i=1 - */ - v = (d[0] + d[1] + d[2] + d[3]) * 0.5f; - // The junction is loaded with the input here. - v += in; - - // Calculate the feed values for the delay lines. - f[0] = v - d[0]; - f[1] = v - d[1]; - f[2] = v - d[2]; - f[3] = v - d[3]; - - // Re-feed the delay lines. - DelayLineIn(&State->Early.Delay[0], State->Offset, f[0]); - DelayLineIn(&State->Early.Delay[1], State->Offset, f[1]); - DelayLineIn(&State->Early.Delay[2], State->Offset, f[2]); - DelayLineIn(&State->Early.Delay[3], State->Offset, f[3]); - - // Output the results of the junction for all four channels. - out[0] = State->Early.Gain * f[0]; - out[1] = State->Early.Gain * f[1]; - out[2] = State->Early.Gain * f[2]; - out[3] = State->Early.Gain * f[3]; + out = DelayLineOut(Delay, outOffset); + feed = feedCoeff * in; + DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in); + + // The time-based attenuation is only applied to the delay output to + // keep it from affecting the feed-back path (which is already controlled + // by the all-pass feed coefficient). + return (coeff * out) - feed; } // All-pass input/output routine for late reverb. -static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint index, ALfloat in) +static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint offset, ALuint index, ALfloat in) { return AllpassInOut(&State->Late.ApDelay[index], - State->Offset - State->Late.ApOffset[index], - State->Offset, in, State->Late.ApFeedCoeff, + offset - State->Late.ApOffset[index], + offset, in, State->Late.ApFeedCoeff, State->Late.ApCoeff[index]); } -// Delay line output routine for late reverb. -static inline ALfloat LateDelayLineOut(ALreverbState *State, ALuint index) -{ - return AttenuatedDelayLineOut(&State->Late.Delay[index], - State->Offset - State->Late.Offset[index], - State->Late.Coeff[index]); -} - // Low-pass filter input/output routine for late reverb. static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALfloat in) { @@ -374,260 +361,296 @@ static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALflo // Given four decorrelated input samples, this function produces four-channel // output for the late reverb. -static inline ALvoid LateReverb(ALreverbState *State, const ALfloat *restrict in, ALfloat *restrict out) +static inline ALvoid LateReverb(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4]) { ALfloat d[4], f[4]; + ALuint i; - // Obtain the decayed results of the cyclical delay lines, and add the - // corresponding input channels. Then pass the results through the - // low-pass filters. - - // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and back - // to 0. - d[0] = LateLowPassInOut(State, 2, in[2] + LateDelayLineOut(State, 2)); - d[1] = LateLowPassInOut(State, 0, in[0] + LateDelayLineOut(State, 0)); - d[2] = LateLowPassInOut(State, 3, in[3] + LateDelayLineOut(State, 3)); - d[3] = LateLowPassInOut(State, 1, in[1] + LateDelayLineOut(State, 1)); - - // To help increase diffusion, run each line through an all-pass filter. - // When there is no diffusion, the shortest all-pass filter will feed the - // shortest delay line. - d[0] = LateAllPassInOut(State, 0, d[0]); - d[1] = LateAllPassInOut(State, 1, d[1]); - d[2] = LateAllPassInOut(State, 2, d[2]); - d[3] = LateAllPassInOut(State, 3, d[3]); - - /* Late reverb is done with a modified feed-back delay network (FDN) - * topology. Four input lines are each fed through their own all-pass - * filter and then into the mixing matrix. The four outputs of the - * mixing matrix are then cycled back to the inputs. Each output feeds - * a different input to form a circlular feed cycle. - * - * The mixing matrix used is a 4D skew-symmetric rotation matrix derived - * using a single unitary rotational parameter: - * - * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2 - * [ -a, d, c, -b ] - * [ -b, -c, d, a ] - * [ -c, b, -a, d ] - * - * The rotation is constructed from the effect's diffusion parameter, - * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y - * with differing signs, and d is the coefficient x. The matrix is thus: - * - * [ x, y, -y, y ] n = sqrt(matrix_order - 1) - * [ -y, x, y, y ] t = diffusion_parameter * atan(n) - * [ y, -y, x, y ] x = cos(t) - * [ -y, -y, -y, x ] y = sin(t) / n - * - * To reduce the number of multiplies, the x coefficient is applied with - * the cyclical delay line coefficients. Thus only the y coefficient is - * applied when mixing, and is modified to be: y / x. - */ - f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3])); - f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3])); - f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3])); - f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] )); - - // Output the results of the matrix for all four channels, attenuated by - // the late reverb gain (which is attenuated by the 'x' mix coefficient). - out[0] = State->Late.Gain * f[0]; - out[1] = State->Late.Gain * f[1]; - out[2] = State->Late.Gain * f[2]; - out[3] = State->Late.Gain * f[3]; - - // Re-feed the cyclical delay lines. - DelayLineIn(&State->Late.Delay[0], State->Offset, f[0]); - DelayLineIn(&State->Late.Delay[1], State->Offset, f[1]); - DelayLineIn(&State->Late.Delay[2], State->Offset, f[2]); - DelayLineIn(&State->Late.Delay[3], State->Offset, f[3]); + for(i = 0;i < todo;i++) + { + ALuint offset = State->Offset+i; + + f[0] = DelayLineOut(&State->Decorrelator, offset); + f[1] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[0]); + f[2] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[1]); + f[3] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[2]); + + // Obtain the decayed results of the cyclical delay lines, and add the + // corresponding input channels. Then pass the results through the + // low-pass filters. + f[0] += DelayLineOut(&State->Late.Delay[0], offset-State->Late.Offset[0]) * State->Late.Coeff[0]; + f[1] += DelayLineOut(&State->Late.Delay[1], offset-State->Late.Offset[1]) * State->Late.Coeff[1]; + f[2] += DelayLineOut(&State->Late.Delay[2], offset-State->Late.Offset[2]) * State->Late.Coeff[2]; + f[3] += DelayLineOut(&State->Late.Delay[3], offset-State->Late.Offset[3]) * State->Late.Coeff[3]; + + // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and + // back to 0. + d[0] = LateLowPassInOut(State, 2, f[2]); + d[1] = LateLowPassInOut(State, 0, f[0]); + d[2] = LateLowPassInOut(State, 3, f[3]); + d[3] = LateLowPassInOut(State, 1, f[1]); + + // To help increase diffusion, run each line through an all-pass filter. + // When there is no diffusion, the shortest all-pass filter will feed + // the shortest delay line. + d[0] = LateAllPassInOut(State, offset, 0, d[0]); + d[1] = LateAllPassInOut(State, offset, 1, d[1]); + d[2] = LateAllPassInOut(State, offset, 2, d[2]); + d[3] = LateAllPassInOut(State, offset, 3, d[3]); + + /* Late reverb is done with a modified feed-back delay network (FDN) + * topology. Four input lines are each fed through their own all-pass + * filter and then into the mixing matrix. The four outputs of the + * mixing matrix are then cycled back to the inputs. Each output feeds + * a different input to form a circlular feed cycle. + * + * The mixing matrix used is a 4D skew-symmetric rotation matrix + * derived using a single unitary rotational parameter: + * + * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2 + * [ -a, d, c, -b ] + * [ -b, -c, d, a ] + * [ -c, b, -a, d ] + * + * The rotation is constructed from the effect's diffusion parameter, + * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y + * with differing signs, and d is the coefficient x. The matrix is + * thus: + * + * [ x, y, -y, y ] n = sqrt(matrix_order - 1) + * [ -y, x, y, y ] t = diffusion_parameter * atan(n) + * [ y, -y, x, y ] x = cos(t) + * [ -y, -y, -y, x ] y = sin(t) / n + * + * To reduce the number of multiplies, the x coefficient is applied + * with the cyclical delay line coefficients. Thus only the y + * coefficient is applied when mixing, and is modified to be: y / x. + */ + f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3])); + f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3])); + f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3])); + f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] )); + + // Output the results of the matrix for all four channels, attenuated by + // the late reverb gain (which is attenuated by the 'x' mix coefficient). + // Mix early reflections and late reverb. + out[i][0] += State->Late.Gain * f[0]; + out[i][1] += State->Late.Gain * f[1]; + out[i][2] += State->Late.Gain * f[2]; + out[i][3] += State->Late.Gain * f[3]; + + // Re-feed the cyclical delay lines. + DelayLineIn(&State->Late.Delay[0], offset, f[0]); + DelayLineIn(&State->Late.Delay[1], offset, f[1]); + DelayLineIn(&State->Late.Delay[2], offset, f[2]); + DelayLineIn(&State->Late.Delay[3], offset, f[3]); + } } // Given an input sample, this function mixes echo into the four-channel late // reverb. -static inline ALvoid EAXEcho(ALreverbState *State, ALfloat in, ALfloat *restrict late) +static inline ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restrict late)[4]) { ALfloat out, feed; + ALuint i; - // Get the latest attenuated echo sample for output. - feed = AttenuatedDelayLineOut(&State->Echo.Delay, - State->Offset - State->Echo.Offset, - State->Echo.Coeff); - - // Mix the output into the late reverb channels. - out = State->Echo.MixCoeff[0] * feed; - late[0] = (State->Echo.MixCoeff[1] * late[0]) + out; - late[1] = (State->Echo.MixCoeff[1] * late[1]) + out; - late[2] = (State->Echo.MixCoeff[1] * late[2]) + out; - late[3] = (State->Echo.MixCoeff[1] * late[3]) + out; - - // Mix the energy-attenuated input with the output and pass it through - // the echo low-pass filter. - feed += State->Echo.DensityGain * in; - feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff); - State->Echo.LpSample = feed; - - // Then the echo all-pass filter. - feed = AllpassInOut(&State->Echo.ApDelay, - State->Offset - State->Echo.ApOffset, - State->Offset, feed, State->Echo.ApFeedCoeff, - State->Echo.ApCoeff); - - // Feed the delay with the mixed and filtered sample. - DelayLineIn(&State->Echo.Delay, State->Offset, feed); + for(i = 0;i < todo;i++) + { + ALuint offset = State->Offset+i; + + // Get the latest attenuated echo sample for output. + feed = DelayLineOut(&State->Echo.Delay, offset-State->Echo.Offset) * + State->Echo.Coeff; + + // Mix the output into the late reverb channels. + out = State->Echo.MixCoeff * feed; + late[i][0] += out; + late[i][1] += out; + late[i][2] += out; + late[i][3] += out; + + // Mix the energy-attenuated input with the output and pass it through + // the echo low-pass filter. + feed += DelayLineOut(&State->Delay, offset-State->DelayTap[1]) * + State->Echo.DensityGain; + feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff); + State->Echo.LpSample = feed; + + // Then the echo all-pass filter. + feed = AllpassInOut(&State->Echo.ApDelay, offset-State->Echo.ApOffset, + offset, feed, State->Echo.ApFeedCoeff, + State->Echo.ApCoeff); + + // Feed the delay with the mixed and filtered sample. + DelayLineIn(&State->Echo.Delay, offset, feed); + } } // Perform the non-EAX reverb pass on a given input sample, resulting in // four-channel output. -static inline ALvoid VerbPass(ALreverbState *State, ALfloat in, ALfloat *restrict out) +static inline ALvoid VerbPass(ALreverbState *State, ALuint todo, const ALfloat *in, ALfloat (*restrict out)[4]) { - ALfloat feed, late[4], taps[4]; + ALuint i; - // Filter the incoming sample. - in = ALfilterState_processSingle(&State->LpFilter, in); - - // Feed the initial delay line. - DelayLineIn(&State->Delay, State->Offset, in); + // Low-pass filter the incoming samples. + for(i = 0;i < todo;i++) + DelayLineIn(&State->Delay, State->Offset+i, + ALfilterState_processSingle(&State->LpFilter, in[i]) + ); // Calculate the early reflection from the first delay tap. - in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); - EarlyReflection(State, in, out); + EarlyReflection(State, todo, out); // Feed the decorrelator from the energy-attenuated output of the second // delay tap. - in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); - feed = in * State->Late.DensityGain; - DelayLineIn(&State->Decorrelator, State->Offset, feed); + for(i = 0;i < todo;i++) + { + ALuint offset = State->Offset+i; + ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) * + State->Late.DensityGain; + DelayLineIn(&State->Decorrelator, offset, sample); + } // Calculate the late reverb from the decorrelator taps. - taps[0] = feed; - taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); - taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); - taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); - LateReverb(State, taps, late); - - // Mix early reflections and late reverb. - out[0] += late[0]; - out[1] += late[1]; - out[2] += late[2]; - out[3] += late[3]; + LateReverb(State, todo, out); // Step all delays forward one sample. - State->Offset++; + State->Offset += todo; } // Perform the EAX reverb pass on a given input sample, resulting in four- // channel output. -static inline ALvoid EAXVerbPass(ALreverbState *State, ALfloat in, ALfloat *restrict early, ALfloat *restrict late) +static inline ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, const ALfloat *input, ALfloat (*restrict early)[4], ALfloat (*restrict late)[4]) { - ALfloat feed, taps[4]; + ALuint i; - // Low-pass filter the incoming sample. - in = ALfilterState_processSingle(&State->LpFilter, in); - in = ALfilterState_processSingle(&State->HpFilter, in); + // Band-pass and modulate the incoming samples. + for(i = 0;i < todo;i++) + { + ALfloat sample = input[i]; + sample = ALfilterState_processSingle(&State->LpFilter, sample); + sample = ALfilterState_processSingle(&State->HpFilter, sample); - // Perform any modulation on the input. - in = EAXModulation(State, in); + // Perform any modulation on the input. + sample = EAXModulation(State, State->Offset+i, sample); - // Feed the initial delay line. - DelayLineIn(&State->Delay, State->Offset, in); + // Feed the initial delay line. + DelayLineIn(&State->Delay, State->Offset+i, sample); + } // Calculate the early reflection from the first delay tap. - in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); - EarlyReflection(State, in, early); + EarlyReflection(State, todo, early); // Feed the decorrelator from the energy-attenuated output of the second // delay tap. - in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); - feed = in * State->Late.DensityGain; - DelayLineIn(&State->Decorrelator, State->Offset, feed); + for(i = 0;i < todo;i++) + { + ALuint offset = State->Offset+i; + ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) * + State->Late.DensityGain; + DelayLineIn(&State->Decorrelator, offset, sample); + } // Calculate the late reverb from the decorrelator taps. - taps[0] = feed; - taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); - taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); - taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); - LateReverb(State, taps, late); + memset(late, 0, sizeof(*late)*todo); + LateReverb(State, todo, late); // Calculate and mix in any echo. - EAXEcho(State, in, late); + EAXEcho(State, todo, late); - // Step all delays forward one sample. - State->Offset++; + // Step all delays forward. + State->Offset += todo; } -static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { ALfloat (*restrict out)[4] = State->ReverbSamples; - ALuint index, c; + ALuint index, c, i, l; /* Process reverb for these samples. */ - for(index = 0;index < SamplesToDo;index++) - VerbPass(State, SamplesIn[index], out[index]); - - for(c = 0;c < MaxChannels;c++) + for(index = 0;index < SamplesToDo;) { - ALfloat gain = State->Gain[c]; - if(!(gain > GAIN_SILENCE_THRESHOLD)) - continue; + ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES); + + VerbPass(State, todo, &SamplesIn[index], out); - for(index = 0;index < SamplesToDo;index++) - SamplesOut[c][index] += gain * out[index][c&3]; + for(l = 0;l < 4;l++) + { + for(c = 0;c < NumChannels;c++) + { + ALfloat gain = State->Gain[l][c]; + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) + continue; + for(i = 0;i < todo;i++) + SamplesOut[c][index+i] += gain*out[i][l]; + } + } + + index += todo; } } -static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { ALfloat (*restrict early)[4] = State->EarlySamples; ALfloat (*restrict late)[4] = State->ReverbSamples; - ALuint index, c; + ALuint index, c, i, l; + ALfloat gain; /* Process reverb for these samples. */ - for(index = 0;index < SamplesToDo;index++) - EAXVerbPass(State, SamplesIn[index], early[index], late[index]); - - for(c = 0;c < MaxChannels;c++) + for(index = 0;index < SamplesToDo;) { - ALfloat earlyGain, lateGain; + ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES); - earlyGain = State->Early.PanGain[c]; - if(earlyGain > GAIN_SILENCE_THRESHOLD) - { - for(index = 0;index < SamplesToDo;index++) - SamplesOut[c][index] += earlyGain*early[index][c&3]; - } - lateGain = State->Late.PanGain[c]; - if(lateGain > GAIN_SILENCE_THRESHOLD) + EAXVerbPass(State, todo, &SamplesIn[index], early, late); + + for(l = 0;l < 4;l++) { - for(index = 0;index < SamplesToDo;index++) - SamplesOut[c][index] += lateGain*late[index][c&3]; + for(c = 0;c < NumChannels;c++) + { + gain = State->Early.PanGain[l][c]; + if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) + { + for(i = 0;i < todo;i++) + SamplesOut[c][index+i] += gain*early[i][l]; + } + gain = State->Late.PanGain[l][c]; + if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) + { + for(i = 0;i < todo;i++) + SamplesOut[c][index+i] += gain*late[i][l]; + } + } } + + index += todo; } } -static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { if(State->IsEax) - ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut); + ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels); else - ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut); + ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels); } // Given the allocated sample buffer, this function updates each delay line // offset. static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay) { - Delay->Line = &sampleBuffer[(ALintptrEXT)Delay->Line]; + Delay->Line = &sampleBuffer[(ptrdiff_t)Delay->Line]; } // Calculate the length of a delay line and store its mask and offset. -static ALuint CalcLineLength(ALfloat length, ALintptrEXT offset, ALuint frequency, DelayLine *Delay) +static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency, ALuint extra, DelayLine *Delay) { ALuint samples; // All line lengths are powers of 2, calculated from their lengths, with // an additional sample in case of rounding errors. - samples = NextPowerOf2(fastf2u(length * frequency) + 1); + samples = fastf2u(length*frequency) + extra; + samples = NextPowerOf2(samples + 1); // All lines share a single sample buffer. Delay->Mask = samples - 1; Delay->Line = (ALfloat*)offset; @@ -654,48 +677,49 @@ static ALboolean AllocLines(ALuint frequency, ALreverbState *State) * swing. An additional sample is added to keep it stable when there is no * modulation. */ - length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f) + - (1.0f / frequency); - totalSamples += CalcLineLength(length, totalSamples, frequency, + length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f); + totalSamples += CalcLineLength(length, totalSamples, frequency, 1, &State->Mod.Delay); // The initial delay is the sum of the reflections and late reverb - // delays. + // delays. This must include space for storing a loop update to feed the + // early reflections, decorrelator, and echo. length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + AL_EAXREVERB_MAX_LATE_REVERB_DELAY; totalSamples += CalcLineLength(length, totalSamples, frequency, - &State->Delay); + MAX_UPDATE_SAMPLES, &State->Delay); // The early reflection lines. for(index = 0;index < 4;index++) totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples, - frequency, &State->Early.Delay[index]); + frequency, 0, &State->Early.Delay[index]); // The decorrelator line is calculated from the lowest reverb density (a - // parameter value of 1). + // parameter value of 1). This must include space for storing a loop update + // to feed the late reverb. length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) * LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER); - totalSamples += CalcLineLength(length, totalSamples, frequency, + totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES, &State->Decorrelator); // The late all-pass lines. for(index = 0;index < 4;index++) totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples, - frequency, &State->Late.ApDelay[index]); + frequency, 0, &State->Late.ApDelay[index]); // The late delay lines are calculated from the lowest reverb density. for(index = 0;index < 4;index++) { length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER); - totalSamples += CalcLineLength(length, totalSamples, frequency, + totalSamples += CalcLineLength(length, totalSamples, frequency, 0, &State->Late.Delay[index]); } // The echo all-pass and delay lines. totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples, - frequency, &State->Echo.ApDelay); + frequency, 0, &State->Echo.ApDelay); totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples, - frequency, &State->Echo.Delay); + frequency, 0, &State->Echo.Delay); if(totalSamples != State->TotalSamples) { @@ -939,7 +963,7 @@ static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALreverbStat } // Update the late reverb gains, line lengths, and line coefficients. -static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State) +static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State) { ALfloat length; ALuint index; @@ -947,9 +971,13 @@ static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix /* Calculate the late reverb gain (from the master effect gain, and late * reverb gain parameters). Since the output is tapped prior to the * application of the next delay line coefficients, this gain needs to be - * attenuated by the 'x' mixing matrix coefficient as well. + * attenuated by the 'x' mixing matrix coefficient as well. Also attenuate + * the late reverb when echo depth is high and diffusion is low, so the + * echo is slightly stronger than the decorrelated echos in the reverb + * tail. */ - State->Late.Gain = reverbGain * lateGain * xMix; + State->Late.Gain = reverbGain * lateGain * xMix * + (1.0f - (echoDepth*0.5f*(1.0f - diffusion))); /* To compensate for changes in modal density and decay time of the late * reverb signal, the input is attenuated based on the maximal energy of @@ -962,8 +990,9 @@ static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] + LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]) / 4.0f; length *= 1.0f + (density * LATE_LINE_MULTIPLIER); - State->Late.DensityGain = CalcDensityGain(CalcDecayCoeff(length, - decayTime)); + State->Late.DensityGain = CalcDensityGain( + CalcDecayCoeff(length, decayTime) + ); // Calculate the all-pass feed-back and feed-forward coefficient. State->Late.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f); @@ -971,12 +1000,13 @@ static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix for(index = 0;index < 4;index++) { // Calculate the gain (coefficient) for each all-pass line. - State->Late.ApCoeff[index] = CalcDecayCoeff(ALLPASS_LINE_LENGTH[index], - decayTime); + State->Late.ApCoeff[index] = CalcDecayCoeff( + ALLPASS_LINE_LENGTH[index], decayTime + ); // Calculate the length (in seconds) of each cyclical delay line. - length = LATE_LINE_LENGTH[index] * (1.0f + (density * - LATE_LINE_MULTIPLIER)); + length = LATE_LINE_LENGTH[index] * + (1.0f + (density * LATE_LINE_MULTIPLIER)); // Calculate the delay offset for each cyclical delay line. State->Late.Offset[index] = fastf2u(length * frequency); @@ -985,9 +1015,9 @@ static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix State->Late.Coeff[index] = CalcDecayCoeff(length, decayTime); // Calculate the damping coefficient for each low-pass filter. - State->Late.LpCoeff[index] = - CalcDampingCoeff(hfRatio, length, decayTime, - State->Late.Coeff[index], cw); + State->Late.LpCoeff[index] = CalcDampingCoeff( + hfRatio, length, decayTime, State->Late.Coeff[index], cw + ); // Attenuate the cyclical line coefficients by the mixing coefficient // (x). @@ -1024,57 +1054,74 @@ static ALvoid UpdateEchoLine(ALfloat reverbGain, ALfloat lateGain, ALfloat echoT * echo depth is high and diffusion is low, so the echo is slightly * stronger than the decorrelated echos in the reverb tail. */ - State->Echo.MixCoeff[0] = reverbGain * lateGain * echoDepth; - State->Echo.MixCoeff[1] = 1.0f - (echoDepth * 0.5f * (1.0f - diffusion)); + State->Echo.MixCoeff = reverbGain * lateGain * echoDepth; } // Update the early and late 3D panning gains. static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALreverbState *State) { - ALfloat earlyPan[3] = { ReflectionsPan[0], ReflectionsPan[1], - ReflectionsPan[2] }; - ALfloat latePan[3] = { LateReverbPan[0], LateReverbPan[1], - LateReverbPan[2] }; - ALfloat ambientGain; - ALfloat dirGain; - ALfloat length; - - Gain *= ReverbBoost; - - /* Attenuate reverb according to its coverage (dirGain=0 will give - * Gain*ambientGain, and dirGain=1 will give Gain). */ - ambientGain = minf(sqrtf(2.0f/Device->NumChan), 1.0f); - - length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2]; - if(length > 1.0f) + static const ALfloat EarlyPanAngles[4] = { + DEG2RAD(0.0f), DEG2RAD(-90.0f), DEG2RAD(90.0f), DEG2RAD(180.0f) + }, LatePanAngles[4] = { + DEG2RAD(45.0f), DEG2RAD(-45.0f), DEG2RAD(135.0f), DEG2RAD(-135.0f) + }; + ALfloat length, ev, az; + ALuint i; + + length = sqrtf(ReflectionsPan[0]*ReflectionsPan[0] + ReflectionsPan[1]*ReflectionsPan[1] + ReflectionsPan[2]*ReflectionsPan[2]); + if(!(length > FLT_EPSILON)) { - length = 1.0f / sqrtf(length); - earlyPan[0] *= length; - earlyPan[1] *= length; - earlyPan[2] *= length; + for(i = 0;i < 4;i++) + ComputeAngleGains(Device, EarlyPanAngles[i], 0.0f, Gain, State->Early.PanGain[i]); } - length = latePan[0]*latePan[0] + latePan[1]*latePan[1] + latePan[2]*latePan[2]; - if(length > 1.0f) + else { - length = 1.0f / sqrtf(length); - latePan[0] *= length; - latePan[1] *= length; - latePan[2] *= length; + ev = asinf(clampf(ReflectionsPan[1]/length, -1.0f, 1.0f)); + az = atan2f(ReflectionsPan[0], ReflectionsPan[2]); + + length = minf(length, 1.0f); + for(i = 0;i < 4;i++) + { + /* This is essentially just a lerp, but takes the shortest path + * with respect to circular wrapping. e.g. + * -135 -> +/-180 -> +135 + * instead of + * -135 -> 0 -> +135 */ + float offset, naz, nev; + naz = EarlyPanAngles[i] + (modff((az-EarlyPanAngles[i])*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU; + nev = (modff((ev )*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU; + ComputeAngleGains(Device, naz, nev, Gain, State->Early.PanGain[i]); + } } - dirGain = sqrtf(earlyPan[0]*earlyPan[0] + earlyPan[2]*earlyPan[2]); - ComputeAngleGains(Device, atan2f(earlyPan[0], earlyPan[2]), (1.0f-dirGain)*F_PI, - lerp(ambientGain, 1.0f, dirGain) * Gain, State->Early.PanGain); + length = sqrtf(LateReverbPan[0]*LateReverbPan[0] + LateReverbPan[1]*LateReverbPan[1] + LateReverbPan[2]*LateReverbPan[2]); + if(!(length > FLT_EPSILON)) + { + for(i = 0;i < 4;i++) + ComputeAngleGains(Device, LatePanAngles[i], 0.0f, Gain, State->Late.PanGain[i]); + } + else + { + ev = asinf(clampf(LateReverbPan[1]/length, -1.0f, 1.0f)); + az = atan2f(LateReverbPan[0], LateReverbPan[2]); - dirGain = sqrtf(latePan[0]*latePan[0] + latePan[2]*latePan[2]); - ComputeAngleGains(Device, atan2f(latePan[0], latePan[2]), (1.0f-dirGain)*F_PI, - lerp(ambientGain, 1.0f, dirGain) * Gain, State->Late.PanGain); + length = minf(length, 1.0f); + for(i = 0;i < 4;i++) + { + float offset, naz, nev; + naz = LatePanAngles[i] + (modff((az-LatePanAngles[i])*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU; + nev = (modff((ev )*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU; + ComputeAngleGains(Device, naz, nev, Gain, State->Late.PanGain[i]); + } + } } static ALvoid ALreverbState_update(ALreverbState *State, ALCdevice *Device, const ALeffectslot *Slot) { + const ALeffectProps *props = &Slot->EffectProps; ALuint frequency = Device->Frequency; ALfloat lfscale, hfscale, hfRatio; + ALfloat gainlf, gainhf; ALfloat cw, x, y; if(Slot->EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb) @@ -1082,85 +1129,60 @@ static ALvoid ALreverbState_update(ALreverbState *State, ALCdevice *Device, cons else if(Slot->EffectType == AL_EFFECT_REVERB || EmulateEAXReverb) State->IsEax = AL_FALSE; - // Calculate the master low-pass filter (from the master effect HF gain). - if(State->IsEax) - { - hfscale = Slot->EffectProps.Reverb.HFReference / frequency; - ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf, - Slot->EffectProps.Reverb.GainHF, - hfscale, 0.0f); - lfscale = Slot->EffectProps.Reverb.LFReference / frequency; - ALfilterState_setParams(&State->HpFilter, ALfilterType_LowShelf, - Slot->EffectProps.Reverb.GainLF, - lfscale, 0.0f); - } - else - { - hfscale = LOWPASSFREQREF / frequency; - ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf, - Slot->EffectProps.Reverb.GainHF, - hfscale, 0.0f); - } - - if(State->IsEax) - { - // Update the modulator line. - UpdateModulator(Slot->EffectProps.Reverb.ModulationTime, - Slot->EffectProps.Reverb.ModulationDepth, - frequency, State); - } + // Calculate the master filters + hfscale = props->Reverb.HFReference / frequency; + gainhf = maxf(props->Reverb.GainHF, 0.0001f); + ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf, + gainhf, hfscale, calc_rcpQ_from_slope(gainhf, 0.75f)); + lfscale = props->Reverb.LFReference / frequency; + gainlf = maxf(props->Reverb.GainLF, 0.0001f); + ALfilterState_setParams(&State->HpFilter, ALfilterType_LowShelf, + gainlf, lfscale, calc_rcpQ_from_slope(gainlf, 0.75f)); + + // Update the modulator line. + UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth, + frequency, State); // Update the initial effect delay. - UpdateDelayLine(Slot->EffectProps.Reverb.ReflectionsDelay, - Slot->EffectProps.Reverb.LateReverbDelay, + UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay, frequency, State); // Update the early lines. - UpdateEarlyLines(Slot->EffectProps.Reverb.Gain, - Slot->EffectProps.Reverb.ReflectionsGain, - Slot->EffectProps.Reverb.LateReverbDelay, State); + UpdateEarlyLines(props->Reverb.Gain, props->Reverb.ReflectionsGain, + props->Reverb.LateReverbDelay, State); // Update the decorrelator. - UpdateDecorrelator(Slot->EffectProps.Reverb.Density, frequency, State); + UpdateDecorrelator(props->Reverb.Density, frequency, State); // Get the mixing matrix coefficients (x and y). - CalcMatrixCoeffs(Slot->EffectProps.Reverb.Diffusion, &x, &y); + CalcMatrixCoeffs(props->Reverb.Diffusion, &x, &y); // Then divide x into y to simplify the matrix calculation. State->Late.MixCoeff = y / x; // If the HF limit parameter is flagged, calculate an appropriate limit // based on the air absorption parameter. - hfRatio = Slot->EffectProps.Reverb.DecayHFRatio; - if(Slot->EffectProps.Reverb.DecayHFLimit && - Slot->EffectProps.Reverb.AirAbsorptionGainHF < 1.0f) - hfRatio = CalcLimitedHfRatio(hfRatio, - Slot->EffectProps.Reverb.AirAbsorptionGainHF, - Slot->EffectProps.Reverb.DecayTime); - - cw = cosf(F_2PI * hfscale); - // Update the late lines. - UpdateLateLines(Slot->EffectProps.Reverb.Gain, Slot->EffectProps.Reverb.LateReverbGain, - x, Slot->EffectProps.Reverb.Density, Slot->EffectProps.Reverb.DecayTime, - Slot->EffectProps.Reverb.Diffusion, hfRatio, cw, frequency, State); + hfRatio = props->Reverb.DecayHFRatio; + if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f) + hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF, + props->Reverb.DecayTime); - if(State->IsEax) - { - // Update the echo line. - UpdateEchoLine(Slot->EffectProps.Reverb.Gain, Slot->EffectProps.Reverb.LateReverbGain, - Slot->EffectProps.Reverb.EchoTime, Slot->EffectProps.Reverb.DecayTime, - Slot->EffectProps.Reverb.Diffusion, Slot->EffectProps.Reverb.EchoDepth, - hfRatio, cw, frequency, State); - - // Update early and late 3D panning. - Update3DPanning(Device, Slot->EffectProps.Reverb.ReflectionsPan, - Slot->EffectProps.Reverb.LateReverbPan, Slot->Gain, State); - } - else - { - /* Update channel gains */ - ALfloat gain = sqrtf(2.0f/Device->NumChan) * ReverbBoost * Slot->Gain; - SetGains(Device, gain, State->Gain); - } + cw = cosf(F_TAU * hfscale); + // Update the late lines. + UpdateLateLines(props->Reverb.Gain, props->Reverb.LateReverbGain, x, + props->Reverb.Density, props->Reverb.DecayTime, + props->Reverb.Diffusion, props->Reverb.EchoDepth, + hfRatio, cw, frequency, State); + + // Update the echo line. + UpdateEchoLine(props->Reverb.Gain, props->Reverb.LateReverbGain, + props->Reverb.EchoTime, props->Reverb.DecayTime, + props->Reverb.Diffusion, props->Reverb.EchoDepth, + hfRatio, cw, frequency, State); + + // Update early and late 3D panning. + Update3DPanning(Device, props->Reverb.ReflectionsPan, + props->Reverb.LateReverbPan, + Slot->Gain * ReverbBoost, State); } @@ -1182,7 +1204,7 @@ typedef struct ALreverbStateFactory { static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory)) { ALreverbState *state; - ALuint index; + ALuint index, l; state = ALreverbState_New(sizeof(*state)); if(!state) return NULL; @@ -1242,10 +1264,13 @@ static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(f state->Late.LpSample[index] = 0.0f; } - for(index = 0;index < MaxChannels;index++) + for(l = 0;l < 4;l++) { - state->Early.PanGain[index] = 0.0f; - state->Late.PanGain[index] = 0.0f; + for(index = 0;index < MAX_OUTPUT_CHANNELS;index++) + { + state->Early.PanGain[l][index] = 0.0f; + state->Late.PanGain[l][index] = 0.0f; + } } state->Echo.DensityGain = 0.0f; @@ -1260,8 +1285,7 @@ static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(f state->Echo.ApOffset = 0; state->Echo.LpCoeff = 0.0f; state->Echo.LpSample = 0.0f; - state->Echo.MixCoeff[0] = 0.0f; - state->Echo.MixCoeff[1] = 0.0f; + state->Echo.MixCoeff = 0.0f; state->Offset = 0; |