Update in properly-sized chunks for PulseAudio
[openal-soft.git] / Alc / alcEcho.c
blobdeaae622ac5718deac98c563ba5ec23128537b8b
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2009 by Chris Robinson.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
26 #include "alMain.h"
27 #include "alFilter.h"
28 #include "alAuxEffectSlot.h"
29 #include "alError.h"
30 #include "alu.h"
32 typedef struct ALechoState {
33 // Must be first in all effects!
34 ALeffectState state;
36 ALfloat *SampleBuffer;
37 ALuint BufferLength;
39 // The echo is two tap. The third tap is the offset to write the feedback
40 // and input sample to
41 struct {
42 ALuint offset;
43 } Tap[3];
44 // The LR gains for the first tap. The second tap uses the reverse
45 ALfloat GainL;
46 ALfloat GainR;
48 ALfloat FeedGain;
50 FILTER iirFilter;
51 ALfloat history[2];
52 } ALechoState;
54 // Find the next power of 2. Actually, this will return the input value if
55 // it is already a power of 2.
56 static ALuint NextPowerOf2(ALuint value)
58 ALuint powerOf2 = 1;
60 if(value)
62 value--;
63 while(value)
65 value >>= 1;
66 powerOf2 <<= 1;
69 return powerOf2;
72 ALvoid EchoDestroy(ALeffectState *effect)
74 ALechoState *state = (ALechoState*)effect;
75 if(state)
77 free(state->SampleBuffer);
78 state->SampleBuffer = NULL;
79 free(state);
83 ALvoid EchoUpdate(ALeffectState *effect, ALCcontext *Context, ALeffect *Effect)
85 ALechoState *state = (ALechoState*)effect;
86 ALuint newdelay1, newdelay2;
87 ALfloat lrpan, cw, a, g;
89 newdelay1 = (ALuint)(Effect->Echo.Delay * Context->Frequency);
90 newdelay2 = (ALuint)(Effect->Echo.LRDelay * Context->Frequency);
92 state->Tap[0].offset = (state->BufferLength - newdelay1 - 1 +
93 state->Tap[2].offset)%state->BufferLength;
94 state->Tap[1].offset = (state->BufferLength - newdelay1 - newdelay2 - 1 +
95 state->Tap[2].offset)%state->BufferLength;
97 lrpan = Effect->Echo.Spread*0.5f + 0.5f;
98 state->GainL = aluSqrt( lrpan);
99 state->GainR = aluSqrt(1.0f-lrpan);
101 state->FeedGain = Effect->Echo.Feedback;
103 cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / Context->Frequency);
104 g = 1.0f - Effect->Echo.Damping;
105 a = 0.0f;
106 if(g < 0.9999f) // 1-epsilon
107 a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
108 state->iirFilter.coeff = a;
111 ALvoid EchoProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
113 ALechoState *state = (ALechoState*)effect;
114 const ALuint delay = state->BufferLength-1;
115 ALuint tap1off = state->Tap[0].offset;
116 ALuint tap2off = state->Tap[1].offset;
117 ALuint fboff = state->Tap[2].offset;
118 ALfloat gain = Slot->Gain;
119 ALfloat samp[2];
120 ALuint i;
122 for(i = 0;i < SamplesToDo;i++)
124 // Apply damping
125 samp[0] = lpFilter2P(&state->iirFilter, 0, state->SampleBuffer[tap2off]+SamplesIn[i]);
127 // Apply feedback gain and mix in the new sample
128 state->SampleBuffer[fboff] = samp[0] * state->FeedGain;
130 tap1off = (tap1off+1) & delay;
131 tap2off = (tap2off+1) & delay;
132 fboff = (fboff+1) & delay;
134 // Sample first tap
135 samp[0] = state->SampleBuffer[tap1off]*state->GainL;
136 samp[1] = state->SampleBuffer[tap1off]*state->GainR;
137 // Sample second tap. Reverse LR panning
138 samp[0] += state->SampleBuffer[tap2off]*state->GainR;
139 samp[1] += state->SampleBuffer[tap2off]*state->GainL;
140 // Apply slot gain
141 samp[0] *= gain;
142 samp[1] *= gain;
144 SamplesOut[i][FRONT_LEFT] += samp[0];
145 SamplesOut[i][FRONT_RIGHT] += samp[1];
146 SamplesOut[i][SIDE_LEFT] += samp[0];
147 SamplesOut[i][SIDE_RIGHT] += samp[1];
148 SamplesOut[i][BACK_LEFT] += samp[0];
149 SamplesOut[i][BACK_RIGHT] += samp[1];
152 state->Tap[0].offset = tap1off;
153 state->Tap[1].offset = tap2off;
154 state->Tap[2].offset = fboff;
157 ALeffectState *EchoCreate(ALCcontext *Context)
159 ALechoState *state;
160 ALuint i, maxlen;
162 state = malloc(sizeof(*state));
163 if(!state)
165 alSetError(AL_OUT_OF_MEMORY);
166 return NULL;
169 state->state.Destroy = EchoDestroy;
170 state->state.Update = EchoUpdate;
171 state->state.Process = EchoProcess;
173 maxlen = (ALuint)(AL_ECHO_MAX_DELAY * Context->Frequency);
174 maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * Context->Frequency);
176 // Use the next power of 2 for the buffer length, so the tap offsets can be
177 // wrapped using a mask instead of a modulo
178 state->BufferLength = NextPowerOf2(maxlen+1);
179 state->SampleBuffer = malloc(state->BufferLength * sizeof(ALfloat));
180 if(!state->SampleBuffer)
182 free(state);
183 alSetError(AL_OUT_OF_MEMORY);
184 return NULL;
187 for(i = 0;i < state->BufferLength;i++)
188 state->SampleBuffer[i] = 0.0f;
190 state->Tap[0].offset = 0;
191 state->Tap[1].offset = 0;
192 state->Tap[2].offset = 0;
193 state->GainL = 0.0f;
194 state->GainR = 0.0f;
196 for(i = 0;i < 2;i++)
197 state->iirFilter.history[i] = 0.0f;
198 state->iirFilter.coeff = 0.0f;
200 return &state->state;