aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/alcEcho.c
diff options
context:
space:
mode:
Diffstat (limited to 'Alc/alcEcho.c')
-rw-r--r--Alc/alcEcho.c102
1 files changed, 59 insertions, 43 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;