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.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
28 #include "alAuxEffectSlot.h"
33 typedef struct ALechoState
{
34 DERIVE_FROM_TYPE(ALeffectState
);
36 ALfloat
*SampleBuffer
;
39 // The echo is two tap. The delay is the number of samples from before the
45 /* The panning gains for the two taps */
46 ALfloat Gain
[2][MAX_OUTPUT_CHANNELS
];
53 static ALvoid
ALechoState_Destruct(ALechoState
*state
);
54 static ALboolean
ALechoState_deviceUpdate(ALechoState
*state
, ALCdevice
*Device
);
55 static ALvoid
ALechoState_update(ALechoState
*state
, const ALCdevice
*Device
, const ALeffectslot
*Slot
, const ALeffectProps
*props
);
56 static ALvoid
ALechoState_process(ALechoState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
57 DECLARE_DEFAULT_ALLOCATORS(ALechoState
)
59 DEFINE_ALEFFECTSTATE_VTABLE(ALechoState
);
62 static void ALechoState_Construct(ALechoState
*state
)
64 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
65 SET_VTABLE2(ALechoState
, ALeffectState
, state
);
67 state
->BufferLength
= 0;
68 state
->SampleBuffer
= NULL
;
70 state
->Tap
[0].delay
= 0;
71 state
->Tap
[1].delay
= 0;
74 ALfilterState_clear(&state
->Filter
);
77 static ALvoid
ALechoState_Destruct(ALechoState
*state
)
79 al_free(state
->SampleBuffer
);
80 state
->SampleBuffer
= NULL
;
81 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
84 static ALboolean
ALechoState_deviceUpdate(ALechoState
*state
, ALCdevice
*Device
)
88 // Use the next power of 2 for the buffer length, so the tap offsets can be
89 // wrapped using a mask instead of a modulo
90 maxlen
= fastf2i(AL_ECHO_MAX_DELAY
* Device
->Frequency
) + 1;
91 maxlen
+= fastf2i(AL_ECHO_MAX_LRDELAY
* Device
->Frequency
) + 1;
92 maxlen
= NextPowerOf2(maxlen
);
94 if(maxlen
!= state
->BufferLength
)
96 void *temp
= al_calloc(16, maxlen
* sizeof(ALfloat
));
97 if(!temp
) return AL_FALSE
;
99 al_free(state
->SampleBuffer
);
100 state
->SampleBuffer
= temp
;
101 state
->BufferLength
= maxlen
;
103 for(i
= 0;i
< state
->BufferLength
;i
++)
104 state
->SampleBuffer
[i
] = 0.0f
;
109 static ALvoid
ALechoState_update(ALechoState
*state
, const ALCdevice
*Device
, const ALeffectslot
*Slot
, const ALeffectProps
*props
)
111 ALuint frequency
= Device
->Frequency
;
112 ALfloat coeffs
[MAX_AMBI_COEFFS
];
113 ALfloat gain
, lrpan
, spread
;
115 state
->Tap
[0].delay
= fastf2i(props
->Echo
.Delay
* frequency
) + 1;
116 state
->Tap
[1].delay
= fastf2i(props
->Echo
.LRDelay
* frequency
);
117 state
->Tap
[1].delay
+= state
->Tap
[0].delay
;
119 spread
= props
->Echo
.Spread
;
120 if(spread
< 0.0f
) lrpan
= -1.0f
;
122 /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
123 * spread (where 0 = point, tau = omni).
125 spread
= asinf(1.0f
- fabsf(spread
))*4.0f
;
127 state
->FeedGain
= props
->Echo
.Feedback
;
129 gain
= maxf(1.0f
- props
->Echo
.Damping
, 0.0625f
); /* Limit -24dB */
130 ALfilterState_setParams(&state
->Filter
, ALfilterType_HighShelf
,
131 gain
, LOWPASSFREQREF
/frequency
,
132 calc_rcpQ_from_slope(gain
, 1.0f
));
134 gain
= Slot
->Params
.Gain
;
136 /* First tap panning */
137 CalcAngleCoeffs(-F_PI_2
*lrpan
, 0.0f
, spread
, coeffs
);
138 ComputePanningGains(Device
->Dry
, coeffs
, gain
, state
->Gain
[0]);
140 /* Second tap panning */
141 CalcAngleCoeffs( F_PI_2
*lrpan
, 0.0f
, spread
, coeffs
);
142 ComputePanningGains(Device
->Dry
, coeffs
, gain
, state
->Gain
[1]);
145 static ALvoid
ALechoState_process(ALechoState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
147 const ALsizei mask
= state
->BufferLength
-1;
148 const ALsizei tap1
= state
->Tap
[0].delay
;
149 const ALsizei tap2
= state
->Tap
[1].delay
;
150 ALsizei offset
= state
->Offset
;
151 ALfloat x
[2], y
[2], in
, out
;
155 x
[0] = state
->Filter
.x
[0];
156 x
[1] = state
->Filter
.x
[1];
157 y
[0] = state
->Filter
.y
[0];
158 y
[1] = state
->Filter
.y
[1];
159 for(base
= 0;base
< SamplesToDo
;)
161 ALfloat temps
[128][2];
162 ALsizei td
= mini(128, SamplesToDo
-base
);
164 for(i
= 0;i
< td
;i
++)
167 temps
[i
][0] = state
->SampleBuffer
[(offset
-tap1
) & mask
];
169 temps
[i
][1] = state
->SampleBuffer
[(offset
-tap2
) & mask
];
171 // Apply damping and feedback gain to the second tap, and mix in the
173 in
= temps
[i
][1] + SamplesIn
[0][i
+base
];
174 out
= in
*state
->Filter
.b0
+
175 x
[0]*state
->Filter
.b1
+ x
[1]*state
->Filter
.b2
-
176 y
[0]*state
->Filter
.a1
- y
[1]*state
->Filter
.a2
;
177 x
[1] = x
[0]; x
[0] = in
;
178 y
[1] = y
[0]; y
[0] = out
;
180 state
->SampleBuffer
[offset
&mask
] = out
* state
->FeedGain
;
184 for(k
= 0;k
< NumChannels
;k
++)
186 ALfloat gain
= state
->Gain
[0][k
];
187 if(fabsf(gain
) > GAIN_SILENCE_THRESHOLD
)
189 for(i
= 0;i
< td
;i
++)
190 SamplesOut
[k
][i
+base
] += temps
[i
][0] * gain
;
193 gain
= state
->Gain
[1][k
];
194 if(fabsf(gain
) > GAIN_SILENCE_THRESHOLD
)
196 for(i
= 0;i
< td
;i
++)
197 SamplesOut
[k
][i
+base
] += temps
[i
][1] * gain
;
203 state
->Filter
.x
[0] = x
[0];
204 state
->Filter
.x
[1] = x
[1];
205 state
->Filter
.y
[0] = y
[0];
206 state
->Filter
.y
[1] = y
[1];
208 state
->Offset
= offset
;
212 typedef struct ALechoStateFactory
{
213 DERIVE_FROM_TYPE(ALeffectStateFactory
);
214 } ALechoStateFactory
;
216 ALeffectState
*ALechoStateFactory_create(ALechoStateFactory
*UNUSED(factory
))
220 NEW_OBJ0(state
, ALechoState
)();
221 if(!state
) return NULL
;
223 return STATIC_CAST(ALeffectState
, state
);
226 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory
);
228 ALeffectStateFactory
*ALechoStateFactory_getFactory(void)
230 static ALechoStateFactory EchoFactory
= { { GET_VTABLE2(ALechoStateFactory
, ALeffectStateFactory
) } };
232 return STATIC_CAST(ALeffectStateFactory
, &EchoFactory
);
236 void ALecho_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
UNUSED(val
))
237 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
238 void ALecho_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
240 ALecho_setParami(effect
, context
, param
, vals
[0]);
242 void ALecho_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
244 ALeffectProps
*props
= &effect
->Props
;
248 if(!(val
>= AL_ECHO_MIN_DELAY
&& val
<= AL_ECHO_MAX_DELAY
))
249 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
250 props
->Echo
.Delay
= val
;
253 case AL_ECHO_LRDELAY
:
254 if(!(val
>= AL_ECHO_MIN_LRDELAY
&& val
<= AL_ECHO_MAX_LRDELAY
))
255 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
256 props
->Echo
.LRDelay
= val
;
259 case AL_ECHO_DAMPING
:
260 if(!(val
>= AL_ECHO_MIN_DAMPING
&& val
<= AL_ECHO_MAX_DAMPING
))
261 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
262 props
->Echo
.Damping
= val
;
265 case AL_ECHO_FEEDBACK
:
266 if(!(val
>= AL_ECHO_MIN_FEEDBACK
&& val
<= AL_ECHO_MAX_FEEDBACK
))
267 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
268 props
->Echo
.Feedback
= val
;
272 if(!(val
>= AL_ECHO_MIN_SPREAD
&& val
<= AL_ECHO_MAX_SPREAD
))
273 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
274 props
->Echo
.Spread
= val
;
278 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
281 void ALecho_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
283 ALecho_setParamf(effect
, context
, param
, vals
[0]);
286 void ALecho_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
*UNUSED(val
))
287 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
288 void ALecho_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
290 ALecho_getParami(effect
, context
, param
, vals
);
292 void ALecho_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
294 const ALeffectProps
*props
= &effect
->Props
;
298 *val
= props
->Echo
.Delay
;
301 case AL_ECHO_LRDELAY
:
302 *val
= props
->Echo
.LRDelay
;
305 case AL_ECHO_DAMPING
:
306 *val
= props
->Echo
.Damping
;
309 case AL_ECHO_FEEDBACK
:
310 *val
= props
->Echo
.Feedback
;
314 *val
= props
->Echo
.Spread
;
318 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
321 void ALecho_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
323 ALecho_getParamf(effect
, context
, param
, vals
);
326 DEFINE_ALEFFECT_VTABLE(ALecho
);