diff options
-rw-r--r-- | Alc/alcEcho.c | 102 | ||||
-rw-r--r-- | Alc/alcReverb.c | 151 | ||||
-rw-r--r-- | OpenAL32/Include/alAuxEffectSlot.h | 8 | ||||
-rw-r--r-- | OpenAL32/alAuxEffectSlot.c | 6 |
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; |