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
28 #include "alAuxEffectSlot.h"
32 #define aluSqrt(x) ((ALfloat)sqrtf((float)(x)))
34 #define aluSqrt(x) ((ALfloat)sqrt((double)(x)))
38 ALfloat
*SampleBuffer
;
41 // The echo is two tap. The third tap is the offset to write the feedback
42 // and input sample to
46 // The LR gains for the first tap. The second tap uses the reverse
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
)
72 ALechoState
*EchoCreate(ALCcontext
*Context
)
77 state
= malloc(sizeof(*state
));
81 maxlen
= (ALuint
)(AL_ECHO_MAX_DELAY
* Context
->Frequency
);
82 maxlen
+= (ALuint
)(AL_ECHO_MAX_LRDELAY
* Context
->Frequency
);
84 // Use the next power of 2 for the buffer length, so the tap offsets can be
85 // wrapped using a mask instead of a modulo
86 state
->BufferLength
= NextPowerOf2(maxlen
+1);
87 state
->SampleBuffer
= malloc(state
->BufferLength
* sizeof(ALfloat
));
88 if(!state
->SampleBuffer
)
94 for(i
= 0;i
< state
->BufferLength
;i
++)
95 state
->SampleBuffer
[i
] = 0.0f
;
97 state
->Tap
[0].offset
= 0;
98 state
->Tap
[1].offset
= 0;
99 state
->Tap
[2].offset
= 0;
103 for(i
= 0;i
< sizeof(state
->iirFilter
.history
)/sizeof(state
->iirFilter
.history
[0]);i
++)
104 state
->iirFilter
.history
[i
] = 0.0f
;
105 state
->iirFilter
.coeff
= 0.0f
;
110 ALvoid
EchoDestroy(ALechoState
*state
)
114 free(state
->SampleBuffer
);
115 state
->SampleBuffer
= NULL
;
120 ALvoid
EchoUpdate(ALCcontext
*Context
, struct ALeffectslot
*Slot
, ALeffect
*Effect
)
122 ALechoState
*state
= Slot
->EchoState
;
123 ALuint newdelay1
, newdelay2
;
124 ALfloat lrpan
, cw
, a
, g
;
126 newdelay1
= (ALuint
)(Effect
->Echo
.Delay
* Context
->Frequency
);
127 newdelay2
= (ALuint
)(Effect
->Echo
.LRDelay
* Context
->Frequency
);
129 state
->Tap
[0].offset
= (state
->BufferLength
- newdelay1
- 1 +
130 state
->Tap
[2].offset
)%state
->BufferLength
;
131 state
->Tap
[1].offset
= (state
->BufferLength
- newdelay1
- newdelay2
- 1 +
132 state
->Tap
[2].offset
)%state
->BufferLength
;
134 lrpan
= Effect
->Echo
.Spread
*0.5f
+ 0.5f
;
135 state
->GainL
= aluSqrt( lrpan
);
136 state
->GainR
= aluSqrt(1.0f
-lrpan
);
138 state
->FeedGain
= Effect
->Echo
.Feedback
;
140 cw
= cos(2.0*M_PI
* LOWPASSFREQCUTOFF
/ Context
->Frequency
);
141 g
= 1.0f
- Effect
->Echo
.Damping
;
143 if(g
< 0.9999f
) // 1-epsilon
144 a
= (1 - g
*cw
- aluSqrt(2*g
*(1-cw
) - g
*g
*(1 - cw
*cw
))) / (1 - g
);
145 state
->iirFilter
.coeff
= a
;
148 ALvoid
EchoProcess(ALechoState
*state
, ALuint SamplesToDo
, const ALfloat
*SamplesIn
, ALfloat (*SamplesOut
)[OUTPUTCHANNELS
])
150 ALfloat
*history
= state
->iirFilter
.history
;
151 const ALfloat a
= state
->iirFilter
.coeff
;
152 const ALuint delay
= state
->BufferLength
-1;
153 ALuint tap1off
= state
->Tap
[0].offset
;
154 ALuint tap2off
= state
->Tap
[1].offset
;
155 ALuint fboff
= state
->Tap
[2].offset
;
159 for(i
= 0;i
< SamplesToDo
;i
++)
162 samp
[0] = state
->SampleBuffer
[tap2off
] + SamplesIn
[i
];
164 samp
[0] += (history
[0]-samp
[0]) * a
;
165 history
[0] = samp
[0];
166 samp
[0] += (history
[1]-samp
[0]) * a
;
167 history
[1] = samp
[0];
169 // Apply feedback gain and mix in the new sample
170 state
->SampleBuffer
[fboff
] = samp
[0] * state
->FeedGain
;
172 tap1off
= (tap1off
+1) & delay
;
173 tap2off
= (tap2off
+1) & delay
;
174 fboff
= (fboff
+1) & delay
;
177 samp
[0] = state
->SampleBuffer
[tap1off
]*state
->GainL
;
178 samp
[1] = state
->SampleBuffer
[tap1off
]*state
->GainR
;
179 // Sample second tap. Reverse LR panning
180 samp
[0] += state
->SampleBuffer
[tap2off
]*state
->GainR
;
181 samp
[1] += state
->SampleBuffer
[tap2off
]*state
->GainL
;
183 SamplesOut
[i
][FRONT_LEFT
] += samp
[0];
184 SamplesOut
[i
][FRONT_RIGHT
] += samp
[1];
185 SamplesOut
[i
][SIDE_LEFT
] += samp
[0];
186 SamplesOut
[i
][SIDE_RIGHT
] += samp
[1];
187 SamplesOut
[i
][BACK_LEFT
] += samp
[0];
188 SamplesOut
[i
][BACK_RIGHT
] += samp
[1];
191 state
->Tap
[0].offset
= tap1off
;
192 state
->Tap
[1].offset
= tap2off
;
193 state
->Tap
[2].offset
= fboff
;