summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2009-10-19 07:46:53 -0700
committerChris Robinson <[email protected]>2009-10-19 07:46:53 -0700
commit6d1d61026d5c5ba75986c2cd8c499bcf72549197 (patch)
treebc23ed399f8694bdbf483eb7221f1a2b7b3982a9
parenta4e3ca933b036102b1aed623015d007869416c7b (diff)
Be context-agnostic in the effect Create functions
This allows the effect Update functions to handle the playback frequency being changed. By default the effects assume a maximum frequency of 192khz, however, it can go higher at the cost of the sample buffers being cleared and the risk of an abort() if reallocation fails
-rw-r--r--Alc/alcEcho.c102
-rw-r--r--Alc/alcReverb.c151
-rw-r--r--OpenAL32/Include/alAuxEffectSlot.h8
-rw-r--r--OpenAL32/alAuxEffectSlot.c6
4 files changed, 171 insertions, 96 deletions
diff --git a/Alc/alcEcho.c b/Alc/alcEcho.c
index deaae622..8aa4d2ce 100644
--- a/Alc/alcEcho.c
+++ b/Alc/alcEcho.c
@@ -29,6 +29,10 @@
#include "alError.h"
#include "alu.h"
+// Just a soft maximum. Being higher will cause EchoUpdate to reallocate the
+// sample buffer which may cause an abort if realloc fails
+#define MAX_ECHO_FREQ 192000
+
typedef struct ALechoState {
// Must be first in all effects!
ALeffectState state;
@@ -36,11 +40,12 @@ typedef struct ALechoState {
ALfloat *SampleBuffer;
ALuint BufferLength;
- // The echo is two tap. The third tap is the offset to write the feedback
- // and input sample to
+ // The echo is two tap. The delay is the number of samples from before the
+ // current offset
struct {
- ALuint offset;
- } Tap[3];
+ ALuint delay;
+ } Tap[2];
+ ALuint Offset;
// The LR gains for the first tap. The second tap uses the reverse
ALfloat GainL;
ALfloat GainR;
@@ -80,19 +85,35 @@ ALvoid EchoDestroy(ALeffectState *effect)
}
}
-ALvoid EchoUpdate(ALeffectState *effect, ALCcontext *Context, ALeffect *Effect)
+ALvoid EchoUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffect *Effect)
{
ALechoState *state = (ALechoState*)effect;
- ALuint newdelay1, newdelay2;
ALfloat lrpan, cw, a, g;
+ ALuint maxlen;
- newdelay1 = (ALuint)(Effect->Echo.Delay * Context->Frequency);
- newdelay2 = (ALuint)(Effect->Echo.LRDelay * Context->Frequency);
+ maxlen = (ALuint)(AL_ECHO_MAX_DELAY * Context->Frequency);
+ maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * Context->Frequency);
+ maxlen = NextPowerOf2(maxlen+1);
+
+ if(maxlen > state->BufferLength)
+ {
+ void *temp;
+ ALuint i;
+
+ state->BufferLength = maxlen;
+ temp = realloc(state->SampleBuffer, state->BufferLength * sizeof(ALfloat));
+ if(!temp)
+ {
+ AL_PRINT("Failed reallocation!");
+ abort();
+ }
+ for(i = 0;i < state->BufferLength;i++)
+ state->SampleBuffer[i] = 0.0f;
+ }
- state->Tap[0].offset = (state->BufferLength - newdelay1 - 1 +
- state->Tap[2].offset)%state->BufferLength;
- state->Tap[1].offset = (state->BufferLength - newdelay1 - newdelay2 - 1 +
- state->Tap[2].offset)%state->BufferLength;
+ state->Tap[0].delay = (ALuint)(Effect->Echo.Delay * Context->Frequency);
+ state->Tap[1].delay = (ALuint)(Effect->Echo.LRDelay * Context->Frequency);
+ state->Tap[1].delay += state->Tap[0].delay;
lrpan = Effect->Echo.Spread*0.5f + 0.5f;
state->GainL = aluSqrt( lrpan);
@@ -111,32 +132,30 @@ ALvoid EchoUpdate(ALeffectState *effect, ALCcontext *Context, ALeffect *Effect)
ALvoid EchoProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
{
ALechoState *state = (ALechoState*)effect;
- const ALuint delay = state->BufferLength-1;
- ALuint tap1off = state->Tap[0].offset;
- ALuint tap2off = state->Tap[1].offset;
- ALuint fboff = state->Tap[2].offset;
- ALfloat gain = Slot->Gain;
- ALfloat samp[2];
+ const ALuint mask = state->BufferLength-1;
+ const ALuint tap1 = state->Tap[0].delay;
+ const ALuint tap2 = state->Tap[1].delay;
+ ALuint offset = state->Offset;
+ const ALfloat gain = Slot->Gain;
+ ALfloat samp[2], smp;
ALuint i;
- for(i = 0;i < SamplesToDo;i++)
+ for(i = 0;i < SamplesToDo;i++,offset++)
{
- // Apply damping
- samp[0] = lpFilter2P(&state->iirFilter, 0, state->SampleBuffer[tap2off]+SamplesIn[i]);
-
- // Apply feedback gain and mix in the new sample
- state->SampleBuffer[fboff] = samp[0] * state->FeedGain;
-
- tap1off = (tap1off+1) & delay;
- tap2off = (tap2off+1) & delay;
- fboff = (fboff+1) & delay;
-
// Sample first tap
- samp[0] = state->SampleBuffer[tap1off]*state->GainL;
- samp[1] = state->SampleBuffer[tap1off]*state->GainR;
+ smp = state->SampleBuffer[(offset-tap1) & mask];
+ samp[0] = smp * state->GainL;
+ samp[1] = smp * state->GainR;
// Sample second tap. Reverse LR panning
- samp[0] += state->SampleBuffer[tap2off]*state->GainR;
- samp[1] += state->SampleBuffer[tap2off]*state->GainL;
+ smp = state->SampleBuffer[(offset-tap2) & mask];
+ samp[0] += smp * state->GainR;
+ samp[1] += smp * state->GainL;
+
+ // Apply damping and feedback gain to the second tap, and mix in the
+ // new sample
+ smp = lpFilter2P(&state->iirFilter, 0, smp+SamplesIn[i]);
+ state->SampleBuffer[offset&mask] = smp * state->FeedGain;
+
// Apply slot gain
samp[0] *= gain;
samp[1] *= gain;
@@ -148,13 +167,10 @@ ALvoid EchoProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint Sampl
SamplesOut[i][BACK_LEFT] += samp[0];
SamplesOut[i][BACK_RIGHT] += samp[1];
}
-
- state->Tap[0].offset = tap1off;
- state->Tap[1].offset = tap2off;
- state->Tap[2].offset = fboff;
+ state->Offset = offset;
}
-ALeffectState *EchoCreate(ALCcontext *Context)
+ALeffectState *EchoCreate(void)
{
ALechoState *state;
ALuint i, maxlen;
@@ -170,8 +186,8 @@ ALeffectState *EchoCreate(ALCcontext *Context)
state->state.Update = EchoUpdate;
state->state.Process = EchoProcess;
- maxlen = (ALuint)(AL_ECHO_MAX_DELAY * Context->Frequency);
- maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * Context->Frequency);
+ maxlen = (ALuint)(AL_ECHO_MAX_DELAY * MAX_ECHO_FREQ);
+ maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * MAX_ECHO_FREQ);
// Use the next power of 2 for the buffer length, so the tap offsets can be
// wrapped using a mask instead of a modulo
@@ -187,9 +203,9 @@ ALeffectState *EchoCreate(ALCcontext *Context)
for(i = 0;i < state->BufferLength;i++)
state->SampleBuffer[i] = 0.0f;
- state->Tap[0].offset = 0;
- state->Tap[1].offset = 0;
- state->Tap[2].offset = 0;
+ state->Tap[0].delay = 0;
+ state->Tap[1].delay = 0;
+ state->Offset = 0;
state->GainL = 0.0f;
state->GainR = 0.0f;
diff --git a/Alc/alcReverb.c b/Alc/alcReverb.c
index 42c823ce..f0d9e0be 100644
--- a/Alc/alcReverb.c
+++ b/Alc/alcReverb.c
@@ -31,6 +31,10 @@
#include "alError.h"
#include "alu.h"
+// Just a soft maximum. Being higher will cause VerbUpdate to reallocate the
+// sample buffer which may cause an abort if realloc fails
+#define MAX_REVERB_FREQ 192000
+
typedef struct DelayLine
{
// The delay lines use sample lengths that are powers of 2 to allow
@@ -46,6 +50,7 @@ typedef struct ALverbState {
// All delay lines are allocated as a single buffer to reduce memory
// fragmentation and management code.
ALfloat *SampleBuffer;
+ ALuint TotalLength;
// Master effect low-pass filter (2 chained 1-pole filters).
FILTER LpFilter;
ALfloat LpHistory[2];
@@ -143,6 +148,46 @@ static ALuint NextPowerOf2(ALuint value)
return powerOf2;
}
+static ALuint CalcLengths(ALuint length[13], ALuint frequency)
+{
+ ALuint samples, totalLength, index;
+
+ // All line lengths are powers of 2, calculated from their lengths, with
+ // an additional sample in case of rounding errors.
+
+ // See VerbUpdate() for an explanation of the additional calculation
+ // added to the master line length.
+ samples = (ALuint)
+ ((MASTER_LINE_LENGTH +
+ (LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER) *
+ (DECO_FRACTION * ((DECO_MULTIPLIER * DECO_MULTIPLIER *
+ DECO_MULTIPLIER) - 1.0f)))) *
+ frequency) + 1;
+ length[0] = NextPowerOf2(samples);
+ totalLength = length[0];
+ for(index = 0;index < 4;index++)
+ {
+ samples = (ALuint)(EARLY_LINE_LENGTH[index] * frequency) + 1;
+ length[1 + index] = NextPowerOf2(samples);
+ totalLength += length[1 + index];
+ }
+ for(index = 0;index < 4;index++)
+ {
+ samples = (ALuint)(ALLPASS_LINE_LENGTH[index] * frequency) + 1;
+ length[5 + index] = NextPowerOf2(samples);
+ totalLength += length[5 + index];
+ }
+ for(index = 0;index < 4;index++)
+ {
+ samples = (ALuint)(LATE_LINE_LENGTH[index] *
+ (1.0f + LATE_LINE_MULTIPLIER) * frequency) + 1;
+ length[9 + index] = NextPowerOf2(samples);
+ totalLength += length[9 + index];
+ }
+
+ return totalLength;
+}
+
// Basic delay line input/output routines.
static __inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset)
{
@@ -369,12 +414,62 @@ static __inline ALint aluCart2LUTpos(ALfloat re, ALfloat im)
// This updates the reverb state. This is called any time the reverb effect
// is loaded into a slot.
-ALvoid VerbUpdate(ALeffectState *effect, ALCcontext *Context, ALeffect *Effect)
+ALvoid VerbUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffect *Effect)
{
ALverbState *State = (ALverbState*)effect;
ALuint index;
ALfloat length, mixCoeff, cw, g, coeff;
ALfloat hfRatio = Effect->Reverb.DecayHFRatio;
+ ALuint lengths[13], totalLength;
+
+ totalLength = CalcLengths(lengths, Context->Frequency);
+ if(totalLength > State->TotalLength)
+ {
+ void *temp;
+
+ State->TotalLength = totalLength;
+ temp = realloc(State->SampleBuffer, State->TotalLength * sizeof(ALfloat));
+ if(!temp)
+ {
+ AL_PRINT("Failed reallocation!");
+ abort();
+ }
+ State->SampleBuffer = temp;
+
+ for(index = 0; index < totalLength;index++)
+ State->SampleBuffer[index] = 0.0f;
+
+ // All lines share a single sample buffer
+ State->Delay.Mask = lengths[0] - 1;
+ State->Delay.Line = &State->SampleBuffer[0];
+ totalLength = lengths[0];
+ for(index = 0;index < 4;index++)
+ {
+ State->Early.Delay[index].Mask = lengths[1 + index] - 1;
+ State->Early.Delay[index].Line = &State->SampleBuffer[totalLength];
+ totalLength += lengths[1 + index];
+ }
+ for(index = 0;index < 4;index++)
+ {
+ State->Late.ApDelay[index].Mask = lengths[5 + index] - 1;
+ State->Late.ApDelay[index].Line = &State->SampleBuffer[totalLength];
+ totalLength += lengths[5 + index];
+ }
+ for(index = 0;index < 4;index++)
+ {
+ State->Late.Delay[index].Mask = lengths[9 + index] - 1;
+ State->Late.Delay[index].Line = &State->SampleBuffer[totalLength];
+ totalLength += lengths[9 + index];
+ }
+ }
+
+ for(index = 0;index < 4;index++)
+ {
+ State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] *
+ Context->Frequency);
+ State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] *
+ Context->Frequency);
+ }
// Calculate the master low-pass filter (from the master effect HF gain).
cw = cos(2.0 * M_PI * Effect->Reverb.HFReference / Context->Frequency);
@@ -659,10 +754,10 @@ ALvoid EAXVerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint Sa
// This creates the reverb state. It should be called only when the reverb
// effect is loaded into a slot that doesn't already have a reverb effect.
-ALeffectState *VerbCreate(ALCcontext *Context)
+ALeffectState *VerbCreate(void)
{
ALverbState *State = NULL;
- ALuint samples, length[13], totalLength, index;
+ ALuint length[13], totalLength, index;
State = malloc(sizeof(ALverbState));
if(!State)
@@ -675,42 +770,10 @@ ALeffectState *VerbCreate(ALCcontext *Context)
State->state.Update = VerbUpdate;
State->state.Process = VerbProcess;
- // All line lengths are powers of 2, calculated from their lengths, with
- // an additional sample in case of rounding errors.
-
- // See VerbUpdate() for an explanation of the additional calculation
- // added to the master line length.
- samples = (ALuint)
- ((MASTER_LINE_LENGTH +
- (LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER) *
- (DECO_FRACTION * ((DECO_MULTIPLIER * DECO_MULTIPLIER *
- DECO_MULTIPLIER) - 1.0f)))) *
- Context->Frequency) + 1;
- length[0] = NextPowerOf2(samples);
- totalLength = length[0];
- for(index = 0;index < 4;index++)
- {
- samples = (ALuint)(EARLY_LINE_LENGTH[index] * Context->Frequency) + 1;
- length[1 + index] = NextPowerOf2(samples);
- totalLength += length[1 + index];
- }
- for(index = 0;index < 4;index++)
- {
- samples = (ALuint)(ALLPASS_LINE_LENGTH[index] * Context->Frequency) + 1;
- length[5 + index] = NextPowerOf2(samples);
- totalLength += length[5 + index];
- }
- for(index = 0;index < 4;index++)
- {
- samples = (ALuint)(LATE_LINE_LENGTH[index] *
- (1.0f + LATE_LINE_MULTIPLIER) * Context->Frequency) + 1;
- length[9 + index] = NextPowerOf2(samples);
- totalLength += length[9 + index];
- }
+ totalLength = CalcLengths(length, MAX_REVERB_FREQ);
- // All lines share a single sample buffer and have their masks and start
- // addresses calculated once.
- State->SampleBuffer = malloc(totalLength * sizeof(ALfloat));
+ State->TotalLength = totalLength;
+ State->SampleBuffer = malloc(State->TotalLength * sizeof(ALfloat));
if(!State->SampleBuffer)
{
free(State);
@@ -741,9 +804,7 @@ ALeffectState *VerbCreate(ALCcontext *Context)
State->Early.Delay[index].Line = &State->SampleBuffer[totalLength];
totalLength += length[1 + index];
- // The early delay lines have their read offsets calculated once.
- State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] *
- Context->Frequency);
+ State->Early.Offset[index] = 0;
}
State->Late.Gain = 0.0f;
@@ -758,9 +819,7 @@ ALeffectState *VerbCreate(ALCcontext *Context)
State->Late.ApDelay[index].Line = &State->SampleBuffer[totalLength];
totalLength += length[5 + index];
- // The late all-pass lines have their read offsets calculated once.
- State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] *
- Context->Frequency);
+ State->Late.ApOffset[index] = 0;
}
for(index = 0;index < 4;index++)
@@ -787,9 +846,9 @@ ALeffectState *VerbCreate(ALCcontext *Context)
return &State->state;
}
-ALeffectState *EAXVerbCreate(ALCcontext *Context)
+ALeffectState *EAXVerbCreate(void)
{
- ALeffectState *State = VerbCreate(Context);
+ ALeffectState *State = VerbCreate();
if(State) State->Process = EAXVerbProcess;
return State;
}
diff --git a/OpenAL32/Include/alAuxEffectSlot.h b/OpenAL32/Include/alAuxEffectSlot.h
index eb41132b..667d74dd 100644
--- a/OpenAL32/Include/alAuxEffectSlot.h
+++ b/OpenAL32/Include/alAuxEffectSlot.h
@@ -55,13 +55,13 @@ ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
struct ALeffectState {
ALvoid (*Destroy)(ALeffectState *State);
- ALvoid (*Update)(ALeffectState *State, ALCcontext *Context, ALeffect *Effect);
+ ALvoid (*Update)(ALeffectState *State, ALCcontext *Context, const ALeffect *Effect);
ALvoid (*Process)(ALeffectState *State, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS]);
};
-ALeffectState *EAXVerbCreate(ALCcontext *Context);
-ALeffectState *VerbCreate(ALCcontext *Context);
-ALeffectState *EchoCreate(ALCcontext *Context);
+ALeffectState *EAXVerbCreate(void);
+ALeffectState *VerbCreate(void);
+ALeffectState *EchoCreate(void);
#define ALEffect_Destroy(a) ((a)->Destroy((a)))
#define ALEffect_Update(a,b,c) ((a)->Update((a),(b),(c)))
diff --git a/OpenAL32/alAuxEffectSlot.c b/OpenAL32/alAuxEffectSlot.c
index acefb624..1fd2f707 100644
--- a/OpenAL32/alAuxEffectSlot.c
+++ b/OpenAL32/alAuxEffectSlot.c
@@ -422,11 +422,11 @@ static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *ALEffectSlot,
if(effect)
{
if(effect->type == AL_EFFECT_EAXREVERB)
- NewState = EAXVerbCreate(Context);
+ NewState = EAXVerbCreate();
else if(effect->type == AL_EFFECT_REVERB)
- NewState = VerbCreate(Context);
+ NewState = VerbCreate();
else if(effect->type == AL_EFFECT_ECHO)
- NewState = EchoCreate(Context);
+ NewState = EchoCreate();
/* No new state? An error occured.. */
if(!NewState)
return;