aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2016-02-06 22:54:03 -0800
committerChris Robinson <[email protected]>2016-02-06 23:00:07 -0800
commit6105d36fd79357053b8bbe7af207a58be8a73f04 (patch)
treeb8054ff7f512d90e6e50916f852184cc49e8bd01 /Alc
parentfd54f4f03d4bad4f06e334089a3707f71f801d6a (diff)
Add special HRTF handling for reverb
This is pretty hacky. Since HRTF normally renders to B-Format with two "extra" channels for the real stereo output, the panning interpolates between a panned reverb channel on B-Format, and two non-panned reverb channels on stereo output, given the panning vector length.
Diffstat (limited to 'Alc')
-rw-r--r--Alc/effects/reverb.c78
1 files changed, 77 insertions, 1 deletions
diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c
index 6f7c7655..d1419d82 100644
--- a/Alc/effects/reverb.c
+++ b/Alc/effects/reverb.c
@@ -48,6 +48,7 @@ typedef struct ALreverbState {
DERIVE_FROM_TYPE(ALeffectState);
ALboolean IsEax;
+ ALuint ExtraChannels; // For HRTF
// All delay lines are allocated as a single buffer to reduce memory
// fragmentation and management code.
@@ -363,6 +364,8 @@ static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Dev
if(!AllocLines(frequency, State))
return AL_FALSE;
+ State->ExtraChannels = (Device->Hrtf ? 2 : 0);
+
// Calculate the modulation filter coefficient. Notice that the exponent
// is calculated given the current sample rate. This ensures that the
// resulting filter response over time is consistent across all sample
@@ -656,6 +659,70 @@ static ALvoid UpdateEchoLine(ALfloat echoTime, ALfloat decayTime, ALfloat diffus
}
// Update the early and late 3D panning gains.
+static ALvoid UpdateHrtfPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALfloat EarlyGain, ALfloat LateGain, ALreverbState *State)
+{
+ ALfloat DirGains[MAX_OUTPUT_CHANNELS];
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+ ALfloat length;
+ ALuint i;
+
+ /* With HRTF, the normal output provides a panned reverb channel when a
+ * non-0-length vector is specified, while the real stereo output provides
+ * two other "direct" non-panned reverb channels.
+ */
+ memset(State->Early.PanGain, 0, sizeof(State->Early.PanGain));
+ length = sqrtf(ReflectionsPan[0]*ReflectionsPan[0] + ReflectionsPan[1]*ReflectionsPan[1] + ReflectionsPan[2]*ReflectionsPan[2]);
+ if(!(length > FLT_EPSILON))
+ {
+ for(i = 0;i < 2;i++)
+ State->Early.PanGain[i&3][Device->NumChannels+i] = Gain * EarlyGain;
+ }
+ else
+ {
+ /* Note that EAX Reverb's panning vectors are using right-handed
+ * coordinates, rather that the OpenAL's left-handed coordinates.
+ * Negate Z to fix this.
+ */
+ ALfloat pan[3] = {
+ ReflectionsPan[0] / length,
+ ReflectionsPan[1] / length,
+ -ReflectionsPan[2] / length,
+ };
+ length = minf(length, 1.0f);
+
+ CalcDirectionCoeffs(pan, coeffs);
+ ComputePanningGains(Device->AmbiCoeffs, Device->NumChannels, coeffs, Gain, DirGains);
+ for(i = 0;i < Device->NumChannels;i++)
+ State->Early.PanGain[3][i] = DirGains[i] * EarlyGain * length;
+ for(i = 0;i < 2;i++)
+ State->Early.PanGain[i&3][Device->NumChannels+i] = Gain * EarlyGain * (1.0f-length);
+ }
+
+ memset(State->Late.PanGain, 0, sizeof(State->Late.PanGain));
+ length = sqrtf(LateReverbPan[0]*LateReverbPan[0] + LateReverbPan[1]*LateReverbPan[1] + LateReverbPan[2]*LateReverbPan[2]);
+ if(!(length > FLT_EPSILON))
+ {
+ for(i = 0;i < 2;i++)
+ State->Late.PanGain[i&3][Device->NumChannels+i] = Gain * LateGain;
+ }
+ else
+ {
+ ALfloat pan[3] = {
+ LateReverbPan[0] / length,
+ LateReverbPan[1] / length,
+ -LateReverbPan[2] / length,
+ };
+ length = minf(length, 1.0f);
+
+ CalcDirectionCoeffs(pan, coeffs);
+ ComputePanningGains(Device->AmbiCoeffs, Device->NumChannels, coeffs, Gain, DirGains);
+ for(i = 0;i < Device->NumChannels;i++)
+ State->Late.PanGain[3][i] = DirGains[i] * LateGain * length;
+ for(i = 0;i < 2;i++)
+ State->Late.PanGain[i&3][Device->NumChannels+i] = Gain * LateGain * (1.0f-length);
+ }
+}
+
static ALvoid UpdateDirectPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALfloat EarlyGain, ALfloat LateGain, ALreverbState *State)
{
ALfloat AmbientGains[MAX_OUTPUT_CHANNELS];
@@ -859,7 +926,12 @@ static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device
gain = props->Reverb.Gain * Slot->Gain * ReverbBoost;
// Update early and late 3D panning.
- if(Device->Hrtf || Device->FmtChans == DevFmtBFormat3D)
+ if(Device->Hrtf)
+ UpdateHrtfPanning(Device, props->Reverb.ReflectionsPan,
+ props->Reverb.LateReverbPan, gain,
+ props->Reverb.ReflectionsGain,
+ props->Reverb.LateReverbGain, State);
+ else if(Device->FmtChans == DevFmtBFormat3D)
Update3DPanning(Device, props->Reverb.ReflectionsPan,
props->Reverb.LateReverbPan, gain,
props->Reverb.ReflectionsGain,
@@ -1280,6 +1352,7 @@ static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo,
static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
{
+ NumChannels += State->ExtraChannels;
if(State->IsEax)
ALreverbState_processEax(State, SamplesToDo, SamplesIn[0], SamplesOut, NumChannels);
else
@@ -1300,6 +1373,9 @@ static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(f
if(!state) return NULL;
SET_VTABLE2(ALreverbState, ALeffectState, state);
+ state->IsEax = AL_FALSE;
+ state->ExtraChannels = 0;
+
state->TotalSamples = 0;
state->SampleBuffer = NULL;