aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2008-01-18 21:25:40 -0800
committerChris Robinson <[email protected]>2008-01-18 21:25:40 -0800
commit4caf2c7edd42bdbc5c1dd2e9084b4fae92c467a7 (patch)
treef415e8f78920a17a67cd4f2d94cec548d298dcc2
parent1b9d740244de7b217c7d3382a7dc3f9bf4c7f403 (diff)
Implement AL_EFFECT_REVERB
Here is a quick description of how the reverb effect works: +--->---+*(4) | V new sample +-----+---+---+ | |extra|ltr|ref| <- +*(1) +-----+---+---+ (3,5)*| |*(2) +-->| V out sample 1) Apply master reverb gain to incoming sample and place it at the head of the buffer. The master reverb gainhf was already applied when the source was initially mixed. 2) Copy the delayed reflection sample to an output sample and apply the reflection gain. 3) Apply the late reverb gain to the late reverb sample 4) Copy the end of the buffer, applying a decay gain and the decay hf ratio, and add to the late reverb. 5) Copy the late reverb sample, adding to the output sample. Then the head and sampling points are shifted forward, and done again for each new sample. The extra buffer length is determined by the Reverb Density property. A value of 0 gives a length of 0.1 seconds (long, with fairly distinct echos) , and 1 gives 0.075 seconds (short, indistinct echos). The decay gain is calculated such that after a number of loops to satisfy the Decay Time, a sample will be 1/32768th as powerful (virtually insignificant to the resulting output, and only getting further reduced). It is calculated as: DecayGain = pow(1.0f/32768.0f, 1.0/(DecayTime/ExtraLength)); Things to note: Reverb Diffusion is not currently handled, nor is Decay HF Limit. Decay HF Ratios above 1 probably give incorrect results. Also, this method likely sucks, but it's the best I can come up with before release. :)
-rw-r--r--Alc/ALu.c117
-rw-r--r--OpenAL32/Include/alAuxEffectSlot.h9
-rw-r--r--OpenAL32/alAuxEffectSlot.c65
3 files changed, 165 insertions, 26 deletions
diff --git a/Alc/ALu.c b/Alc/ALu.c
index d7bd5aa0..c6df899c 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -311,6 +311,12 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
//2. Calculate distance attenuation
Distance = aluSqrt(aluDotproduct(Position, Position));
+ if(ALSource->Send[0].Slot && !ALSource->Send[0].Slot->AuxSendAuto)
+ {
+ if(ALSource->Send[0].Slot->effect.type == AL_EFFECT_REVERB)
+ RoomRolloff = ALSource->Send[0].Slot->effect.Reverb.RoomRolloffFactor;
+ }
+
flAttenuation = 1.0f;
RoomAttenuation = 1.0f;
switch (ALContext->DistanceModel)
@@ -371,14 +377,11 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
DryMix = SourceVolume * flAttenuation;
DryMix = __min(DryMix,MaxVolume);
DryMix = __max(DryMix,MinVolume);
- if(ALSource->Send[0].Slot)
- {
- WetMix = SourceVolume * ((ALSource->WetGainAuto &&
- ALSource->Send[0].Slot->AuxSendAuto) ?
- RoomAttenuation : 1.0f);
- WetMix = __min(WetMix,MaxVolume);
- WetMix = __max(WetMix,MinVolume);
- }
+
+ WetMix = SourceVolume * (ALSource->WetGainAuto ?
+ RoomAttenuation : 1.0f);
+ WetMix = __min(WetMix,MaxVolume);
+ WetMix = __max(WetMix,MinVolume);
//3. Apply directional soundcones
SourceToListener[0] = -Position[0];
@@ -473,6 +476,18 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
DryGainHF *= pow(ALSource->AirAbsorptionFactor * AIRABSORBGAINHF,
Distance * MetersPerUnit);
+ if(ALSource->Send[0].Slot)
+ {
+ WetMix *= ALSource->Send[0].Slot->Gain;
+
+ if(ALSource->Send[0].Slot->effect.type == AL_EFFECT_REVERB)
+ {
+ WetGainHF *= ALSource->Send[0].Slot->effect.Reverb.GainHF;
+ WetGainHF *= pow(ALSource->Send[0].Slot->effect.Reverb.AirAbsorptionGainHF,
+ Distance * MetersPerUnit);
+ }
+ }
+
//7. Convert normalized position into pannings, then into channel volumes
aluNormalize(Position);
switch(aluChannelsFromFormat(OutputFormat))
@@ -482,7 +497,6 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
drysend[FRONT_RIGHT] = ConeVolume * ListenerGain * DryMix * aluSqrt(1.0f); //Direct
if(ALSource->Send[0].Slot)
{
- WetMix *= ALSource->Send[0].Slot->Gain;
wetsend[FRONT_LEFT] = ListenerGain * WetMix * aluSqrt(1.0f); //Room
wetsend[FRONT_RIGHT] = ListenerGain * WetMix * aluSqrt(1.0f); //Room
}
@@ -499,7 +513,6 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
drysend[FRONT_RIGHT] = ConeVolume * ListenerGain * DryMix * aluSqrt( PanningLR); //R Direct
if(ALSource->Send[0].Slot)
{
- WetMix *= ALSource->Send[0].Slot->Gain;
wetsend[FRONT_LEFT] = ListenerGain * WetMix * aluSqrt(1.0f-PanningLR); //L Room
wetsend[FRONT_RIGHT] = ListenerGain * WetMix * aluSqrt( PanningLR); //R Room
}
@@ -526,7 +539,6 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
drysend[BACK_RIGHT] = ConeVolume * ListenerGain * DryMix * aluSqrt(( PanningLR)*( PanningFB));
if(ALSource->Send[0].Slot)
{
- WetMix *= ALSource->Send[0].Slot->Gain;
wetsend[FRONT_LEFT] = ListenerGain * WetMix * aluSqrt((1.0f-PanningLR)*(1.0f-PanningFB));
wetsend[FRONT_RIGHT] = ListenerGain * WetMix * aluSqrt(( PanningLR)*(1.0f-PanningFB));
wetsend[BACK_LEFT] = ListenerGain * WetMix * aluSqrt((1.0f-PanningLR)*( PanningFB));
@@ -559,7 +571,6 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
drysend[FRONT_RIGHT] = 0.0f;
if(ALSource->Send[0].Slot)
{
- WetMix *= ALSource->Send[0].Slot->Gain;
wetsend[BACK_LEFT] = ListenerGain * WetMix * aluSqrt((1.0f-PanningLR)*(1.0f-PanningFB));
wetsend[BACK_RIGHT] = ListenerGain * WetMix * aluSqrt(( PanningLR)*(1.0f-PanningFB));
wetsend[SIDE_LEFT] = ListenerGain * WetMix * aluSqrt((1.0f-PanningLR)*( PanningFB));
@@ -588,7 +599,6 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
drysend[BACK_RIGHT] = 0.0f;
if(ALSource->Send[0].Slot)
{
- WetMix *= ALSource->Send[0].Slot->Gain;
wetsend[FRONT_LEFT] = ListenerGain * WetMix * aluSqrt((1.0f-PanningLR)*(1.0f-PanningFB));
wetsend[FRONT_RIGHT] = ListenerGain * WetMix * aluSqrt(( PanningLR)*(1.0f-PanningFB));
wetsend[SIDE_LEFT] = ListenerGain * WetMix * aluSqrt((1.0f-PanningLR)*( PanningFB));
@@ -660,6 +670,7 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
{
static float DryBuffer[BUFFERSIZE][OUTPUTCHANNELS];
static float WetBuffer[BUFFERSIZE][OUTPUTCHANNELS];
+ static float ReverbBuffer[BUFFERSIZE];
ALfloat DrySend[OUTPUTCHANNELS] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
ALfloat WetSend[OUTPUTCHANNELS] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
ALfloat DryGainHF = 0.0f;
@@ -667,12 +678,14 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
ALuint BlockAlign,BufferSize;
ALuint DataSize=0,DataPosInt=0,DataPosFrac=0;
ALuint Channels,Frequency,ulExtraSamples;
+ ALboolean doReverb;
ALfloat Pitch;
ALint Looping,increment,State;
ALuint Buffer,fraction;
ALuint SamplesToDo;
ALsource *ALSource;
ALbuffer *ALBuffer;
+ ALeffectslot *ALEffectSlot;
ALfloat value;
ALshort *Data;
ALuint i,j,k;
@@ -698,12 +711,18 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
//Clear mixing buffer
memset(DryBuffer, 0, SamplesToDo*OUTPUTCHANNELS*sizeof(ALfloat));
memset(WetBuffer, 0, SamplesToDo*OUTPUTCHANNELS*sizeof(ALfloat));
+ memset(ReverbBuffer, 0, SamplesToDo*sizeof(ALfloat));
//Actual mixing loop
while(ALSource)
{
j = 0;
State = ALSource->state;
+
+ doReverb = ((ALSource->Send[0].Slot &&
+ ALSource->Send[0].Slot->effect.type == AL_EFFECT_REVERB) ?
+ AL_TRUE : AL_FALSE);
+
while(State == AL_PLAYING && j < SamplesToDo)
{
DataSize = 0;
@@ -798,12 +817,17 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
DryBuffer[j][BACK_RIGHT] += value*DrySend[BACK_RIGHT];
//Room path final mix buffer and panning
value = aluComputeWetSample(ALSource, WetGainHF, sample);
- WetBuffer[j][FRONT_LEFT] += value*WetSend[FRONT_LEFT];
- WetBuffer[j][FRONT_RIGHT] += value*WetSend[FRONT_RIGHT];
- WetBuffer[j][SIDE_LEFT] += value*WetSend[SIDE_LEFT];
- WetBuffer[j][SIDE_RIGHT] += value*WetSend[SIDE_RIGHT];
- WetBuffer[j][BACK_LEFT] += value*WetSend[BACK_LEFT];
- WetBuffer[j][BACK_RIGHT] += value*WetSend[BACK_RIGHT];
+ if(doReverb)
+ ReverbBuffer[j] += value;
+ else
+ {
+ WetBuffer[j][FRONT_LEFT] += value*WetSend[FRONT_LEFT];
+ WetBuffer[j][FRONT_RIGHT] += value*WetSend[FRONT_RIGHT];
+ WetBuffer[j][SIDE_LEFT] += value*WetSend[SIDE_LEFT];
+ WetBuffer[j][SIDE_RIGHT] += value*WetSend[SIDE_RIGHT];
+ WetBuffer[j][BACK_LEFT] += value*WetSend[BACK_LEFT];
+ WetBuffer[j][BACK_RIGHT] += value*WetSend[BACK_RIGHT];
+ }
}
else
{
@@ -945,6 +969,61 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
ALSource = ALSource->next;
}
+ ALEffectSlot = (ALContext ? ALContext->AuxiliaryEffectSlot : NULL);
+ while(ALEffectSlot)
+ {
+ if(ALEffectSlot->effect.type == AL_EFFECT_REVERB)
+ {
+ ALfloat *DelayBuffer = ALEffectSlot->ReverbBuffer;
+ ALuint Pos = ALEffectSlot->ReverbPos;
+ ALuint LatePos = ALEffectSlot->ReverbLatePos;
+ ALuint ReflectPos = ALEffectSlot->ReverbReflectPos;
+ ALuint Length = ALEffectSlot->ReverbLength;
+ ALfloat DecayGain = ALEffectSlot->ReverbDecayGain;
+ ALfloat DecayHFRatio = ALEffectSlot->effect.Reverb.DecayHFRatio;
+ ALfloat Gain = ALEffectSlot->effect.Reverb.Gain;
+ ALfloat ReflectGain = ALEffectSlot->effect.Reverb.ReflectionsGain;
+ ALfloat LateReverbGain = ALEffectSlot->effect.Reverb.LateReverbGain;
+ ALfloat LastDecaySample = ALEffectSlot->LastDecaySample;
+ ALfloat sample;
+
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ DelayBuffer[Pos] = ReverbBuffer[i] * Gain;
+
+ sample = DelayBuffer[ReflectPos] * ReflectGain;
+
+ DelayBuffer[LatePos] *= LateReverbGain;
+
+ Pos = (Pos+1) % Length;
+ DelayBuffer[Pos] *= DecayHFRatio;
+ DelayBuffer[Pos] += LastDecaySample * (1.0f-DecayHFRatio);
+ LastDecaySample = DelayBuffer[Pos];
+ DelayBuffer[Pos] *= DecayGain;
+
+ DelayBuffer[LatePos] += DelayBuffer[Pos];
+
+ sample += DelayBuffer[LatePos];
+
+ WetBuffer[i][FRONT_LEFT] += sample;
+ WetBuffer[i][FRONT_RIGHT] += sample;
+ WetBuffer[i][SIDE_LEFT] += sample;
+ WetBuffer[i][SIDE_RIGHT] += sample;
+ WetBuffer[i][BACK_LEFT] += sample;
+ WetBuffer[i][BACK_RIGHT] += sample;
+
+ LatePos = (LatePos+1) % Length;
+ ReflectPos = (ReflectPos+1) % Length;
+ }
+
+ ALEffectSlot->ReverbPos = Pos;
+ ALEffectSlot->ReverbLatePos = LatePos;
+ ALEffectSlot->ReverbReflectPos = ReflectPos;
+ ALEffectSlot->LastDecaySample = LastDecaySample;
+ }
+ ALEffectSlot = ALEffectSlot->next;
+ }
+
//Post processing loop
switch(format)
{
diff --git a/OpenAL32/Include/alAuxEffectSlot.h b/OpenAL32/Include/alAuxEffectSlot.h
index 66b3ba20..d4cb0d11 100644
--- a/OpenAL32/Include/alAuxEffectSlot.h
+++ b/OpenAL32/Include/alAuxEffectSlot.h
@@ -21,6 +21,15 @@ typedef struct ALeffectslot
ALfloat Gain;
ALboolean AuxSendAuto;
+ ALfloat *ReverbBuffer;
+ // in frames!
+ ALuint ReverbLength;
+ ALuint ReverbPos;
+ ALuint ReverbReflectPos;
+ ALuint ReverbLatePos;
+ ALfloat ReverbDecayGain;
+ ALfloat LastDecaySample;
+
ALuint refcount;
// Index to itself
diff --git a/OpenAL32/alAuxEffectSlot.c b/OpenAL32/alAuxEffectSlot.c
index 3ce845ce..41ea6c72 100644
--- a/OpenAL32/alAuxEffectSlot.c
+++ b/OpenAL32/alAuxEffectSlot.c
@@ -21,6 +21,7 @@
#include "config.h"
#include <stdlib.h>
+#include <math.h>
#include "AL/al.h"
#include "AL/alc.h"
@@ -30,6 +31,9 @@
#include "alError.h"
+static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *ALEffectSlot, ALeffect *effect);
+
+
AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
{
ALCcontext *Context;
@@ -144,6 +148,8 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, ALuint *effect
*list = (*list)->next;
ALTHUNK_REMOVEENTRY(ALAuxiliaryEffectSlot->effectslot);
+ free(ALAuxiliaryEffectSlot->ReverbBuffer);
+
memset(ALAuxiliaryEffectSlot, 0, sizeof(ALeffectslot));
free(ALAuxiliaryEffectSlot);
@@ -202,13 +208,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param
if(alIsEffect(iValue))
{
ALeffect *effect = (ALeffect*)ALTHUNK_LOOKUPENTRY(iValue);
- if(!effect)
- {
- ALEffectSlot->effect.type = AL_EFFECT_NULL;
- ALEffectSlot->effect.effect = 0;
- }
- else
- memcpy(&ALEffectSlot->effect, effect, sizeof(*effect));
+ InitializeEffect(Context, ALEffectSlot, effect);
}
else
alSetError(AL_INVALID_VALUE);
@@ -465,6 +465,55 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum p
}
+static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *ALEffectSlot, ALeffect *effect)
+{
+ ALfloat *ptr = NULL;
+
+ if(!effect)
+ {
+ memset(&ALEffectSlot->effect, 0, sizeof(ALEffectSlot->effect));
+ goto done;
+ }
+
+ if(effect->type == AL_EFFECT_REVERB)
+ {
+ ALuint size;
+ ALfloat reverbwait;
+
+ reverbwait = (1.0f-effect->Reverb.Density)*(0.1f-0.075f) + 0.075f;
+
+ size = (ALuint)((ALfloat)Context->Frequency *
+ (effect->Reverb.ReflectionsDelay +
+ effect->Reverb.LateReverbDelay +
+ reverbwait)) + 1;
+
+ ptr = calloc(size, sizeof(ALfloat));
+ if(!ptr)
+ {
+ alSetError(AL_OUT_OF_MEMORY);
+ return;
+ }
+ ALEffectSlot->ReverbLength = size;
+ ALEffectSlot->ReverbPos = 0;
+ ALEffectSlot->ReverbReflectPos = (ALuint)(ALEffectSlot->ReverbLength -
+ ((ALfloat)Context->Frequency *
+ effect->Reverb.ReflectionsDelay)) %
+ ALEffectSlot->ReverbLength;
+ ALEffectSlot->ReverbLatePos = (ALuint)(ALEffectSlot->ReverbLength -
+ ((ALfloat)Context->Frequency *
+ (effect->Reverb.LateReverbDelay +
+ effect->Reverb.ReflectionsDelay))) %
+ ALEffectSlot->ReverbLength;
+ ALEffectSlot->ReverbDecayGain = pow(1.0/32768.0, 1.0/(effect->Reverb.DecayTime/reverbwait));
+ }
+
+ memcpy(&ALEffectSlot->effect, effect, sizeof(*effect));
+done:
+ free(ALEffectSlot->ReverbBuffer);
+ ALEffectSlot->ReverbBuffer = ptr;
+}
+
+
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
{
#ifdef _DEBUG
@@ -478,6 +527,8 @@ ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
Context->AuxiliaryEffectSlot = Context->AuxiliaryEffectSlot->next;
// Release effectslot structure
+ free(temp->ReverbBuffer);
+
memset(temp, 0, sizeof(ALeffectslot));
free(temp);
}