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
)
55 al_free(state
->SampleBuffer
);
56 state
->SampleBuffer
= NULL
;
57 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
60 static ALboolean
ALechoState_deviceUpdate(ALechoState
*state
, ALCdevice
*Device
)
64 // Use the next power of 2 for the buffer length, so the tap offsets can be
65 // wrapped using a mask instead of a modulo
66 maxlen
= fastf2u(AL_ECHO_MAX_DELAY
* Device
->Frequency
) + 1;
67 maxlen
+= fastf2u(AL_ECHO_MAX_LRDELAY
* Device
->Frequency
) + 1;
68 maxlen
= NextPowerOf2(maxlen
);
70 if(maxlen
!= state
->BufferLength
)
72 void *temp
= al_calloc(16, maxlen
* sizeof(ALfloat
));
73 if(!temp
) return AL_FALSE
;
75 al_free(state
->SampleBuffer
);
76 state
->SampleBuffer
= temp
;
77 state
->BufferLength
= maxlen
;
79 for(i
= 0;i
< state
->BufferLength
;i
++)
80 state
->SampleBuffer
[i
] = 0.0f
;
85 static ALvoid
ALechoState_update(ALechoState
*state
, const ALCdevice
*Device
, const ALeffectslot
*Slot
, const ALeffectProps
*props
)
87 ALuint frequency
= Device
->Frequency
;
88 ALfloat coeffs
[MAX_AMBI_COEFFS
];
89 ALfloat gain
, lrpan
, spread
;
91 state
->Tap
[0].delay
= fastf2u(props
->Echo
.Delay
* frequency
) + 1;
92 state
->Tap
[1].delay
= fastf2u(props
->Echo
.LRDelay
* frequency
);
93 state
->Tap
[1].delay
+= state
->Tap
[0].delay
;
95 spread
= props
->Echo
.Spread
;
96 if(spread
< 0.0f
) lrpan
= -1.0f
;
98 /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
99 * spread (where 0 = point, tau = omni).
101 spread
= asinf(1.0f
- fabsf(spread
))*4.0f
;
103 state
->FeedGain
= props
->Echo
.Feedback
;
105 gain
= minf(1.0f
- props
->Echo
.Damping
, 0.01f
);
106 ALfilterState_setParams(&state
->Filter
, ALfilterType_HighShelf
,
107 gain
, LOWPASSFREQREF
/frequency
,
108 calc_rcpQ_from_slope(gain
, 0.75f
));
110 gain
= Slot
->Params
.Gain
;
112 /* First tap panning */
113 CalcXYZCoeffs(-lrpan
, 0.0f
, 0.0f
, spread
, coeffs
);
114 ComputePanningGains(Device
->Dry
, coeffs
, gain
, state
->Gain
[0]);
116 /* Second tap panning */
117 CalcXYZCoeffs( lrpan
, 0.0f
, 0.0f
, spread
, coeffs
);
118 ComputePanningGains(Device
->Dry
, coeffs
, gain
, state
->Gain
[1]);
121 static ALvoid
ALechoState_process(ALechoState
*state
, ALuint SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALuint NumChannels
)
123 const ALuint mask
= state
->BufferLength
-1;
124 const ALuint tap1
= state
->Tap
[0].delay
;
125 const ALuint tap2
= state
->Tap
[1].delay
;
126 ALuint offset
= state
->Offset
;
127 ALfloat x
[2], y
[2], in
, out
;
131 x
[0] = state
->Filter
.x
[0];
132 x
[1] = state
->Filter
.x
[1];
133 y
[0] = state
->Filter
.y
[0];
134 y
[1] = state
->Filter
.y
[1];
135 for(base
= 0;base
< SamplesToDo
;)
137 ALfloat temps
[128][2];
138 ALuint td
= minu(128, SamplesToDo
-base
);
140 for(i
= 0;i
< td
;i
++)
143 temps
[i
][0] = state
->SampleBuffer
[(offset
-tap1
) & mask
];
145 temps
[i
][1] = state
->SampleBuffer
[(offset
-tap2
) & mask
];
147 // Apply damping and feedback gain to the second tap, and mix in the
149 in
= temps
[i
][1] + SamplesIn
[0][i
+base
];
150 out
= in
*state
->Filter
.b0
+
151 x
[0]*state
->Filter
.b1
+ x
[1]*state
->Filter
.b2
-
152 y
[0]*state
->Filter
.a1
- y
[1]*state
->Filter
.a2
;
153 x
[1] = x
[0]; x
[0] = in
;
154 y
[1] = y
[0]; y
[0] = out
;
156 state
->SampleBuffer
[offset
&mask
] = out
* state
->FeedGain
;
160 for(k
= 0;k
< NumChannels
;k
++)
162 ALfloat gain
= state
->Gain
[0][k
];
163 if(fabsf(gain
) > GAIN_SILENCE_THRESHOLD
)
165 for(i
= 0;i
< td
;i
++)
166 SamplesOut
[k
][i
+base
] += temps
[i
][0] * gain
;
169 gain
= state
->Gain
[1][k
];
170 if(fabsf(gain
) > GAIN_SILENCE_THRESHOLD
)
172 for(i
= 0;i
< td
;i
++)
173 SamplesOut
[k
][i
+base
] += temps
[i
][1] * gain
;
179 state
->Filter
.x
[0] = x
[0];
180 state
->Filter
.x
[1] = x
[1];
181 state
->Filter
.y
[0] = y
[0];
182 state
->Filter
.y
[1] = y
[1];
184 state
->Offset
= offset
;
187 DECLARE_DEFAULT_ALLOCATORS(ALechoState
)
189 DEFINE_ALEFFECTSTATE_VTABLE(ALechoState
);
192 typedef struct ALechoStateFactory
{
193 DERIVE_FROM_TYPE(ALeffectStateFactory
);
194 } ALechoStateFactory
;
196 ALeffectState
*ALechoStateFactory_create(ALechoStateFactory
*UNUSED(factory
))
200 state
= ALechoState_New(sizeof(*state
));
201 if(!state
) return NULL
;
202 SET_VTABLE2(ALechoState
, ALeffectState
, state
);
204 state
->BufferLength
= 0;
205 state
->SampleBuffer
= NULL
;
207 state
->Tap
[0].delay
= 0;
208 state
->Tap
[1].delay
= 0;
211 ALfilterState_clear(&state
->Filter
);
213 return STATIC_CAST(ALeffectState
, state
);
216 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory
);
218 ALeffectStateFactory
*ALechoStateFactory_getFactory(void)
220 static ALechoStateFactory EchoFactory
= { { GET_VTABLE2(ALechoStateFactory
, ALeffectStateFactory
) } };
222 return STATIC_CAST(ALeffectStateFactory
, &EchoFactory
);
226 void ALecho_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
UNUSED(val
))
227 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
228 void ALecho_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
230 ALecho_setParami(effect
, context
, param
, vals
[0]);
232 void ALecho_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
234 ALeffectProps
*props
= &effect
->Props
;
238 if(!(val
>= AL_ECHO_MIN_DELAY
&& val
<= AL_ECHO_MAX_DELAY
))
239 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
240 props
->Echo
.Delay
= val
;
243 case AL_ECHO_LRDELAY
:
244 if(!(val
>= AL_ECHO_MIN_LRDELAY
&& val
<= AL_ECHO_MAX_LRDELAY
))
245 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
246 props
->Echo
.LRDelay
= val
;
249 case AL_ECHO_DAMPING
:
250 if(!(val
>= AL_ECHO_MIN_DAMPING
&& val
<= AL_ECHO_MAX_DAMPING
))
251 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
252 props
->Echo
.Damping
= val
;
255 case AL_ECHO_FEEDBACK
:
256 if(!(val
>= AL_ECHO_MIN_FEEDBACK
&& val
<= AL_ECHO_MAX_FEEDBACK
))
257 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
258 props
->Echo
.Feedback
= val
;
262 if(!(val
>= AL_ECHO_MIN_SPREAD
&& val
<= AL_ECHO_MAX_SPREAD
))
263 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
264 props
->Echo
.Spread
= val
;
268 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
271 void ALecho_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
273 ALecho_setParamf(effect
, context
, param
, vals
[0]);
276 void ALecho_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
*UNUSED(val
))
277 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
278 void ALecho_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
280 ALecho_getParami(effect
, context
, param
, vals
);
282 void ALecho_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
284 const ALeffectProps
*props
= &effect
->Props
;
288 *val
= props
->Echo
.Delay
;
291 case AL_ECHO_LRDELAY
:
292 *val
= props
->Echo
.LRDelay
;
295 case AL_ECHO_DAMPING
:
296 *val
= props
->Echo
.Damping
;
299 case AL_ECHO_FEEDBACK
:
300 *val
= props
->Echo
.Feedback
;
304 *val
= props
->Echo
.Spread
;
308 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
311 void ALecho_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
313 ALecho_getParamf(effect
, context
, param
, vals
);
316 DEFINE_ALEFFECT_VTABLE(ALecho
);