aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/effects
diff options
context:
space:
mode:
Diffstat (limited to 'Alc/effects')
-rw-r--r--Alc/effects/autowah.c26
-rw-r--r--Alc/effects/chorus.c26
-rw-r--r--Alc/effects/compressor.c25
-rw-r--r--Alc/effects/dedicated.c42
-rw-r--r--Alc/effects/distortion.c38
-rw-r--r--Alc/effects/echo.c34
-rw-r--r--Alc/effects/equalizer.c53
-rw-r--r--Alc/effects/flanger.c26
-rw-r--r--Alc/effects/modulator.c33
-rw-r--r--Alc/effects/null.c5
-rw-r--r--Alc/effects/reverb.c826
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;