diff options
author | Chris Robinson <[email protected]> | 2009-10-19 07:46:53 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2009-10-19 07:46:53 -0700 |
commit | 6d1d61026d5c5ba75986c2cd8c499bcf72549197 (patch) | |
tree | bc23ed399f8694bdbf483eb7221f1a2b7b3982a9 /Alc | |
parent | a4e3ca933b036102b1aed623015d007869416c7b (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
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/alcEcho.c | 102 | ||||
-rw-r--r-- | Alc/alcReverb.c | 151 |
2 files changed, 164 insertions, 89 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; } |