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"
31 #include "filters/defs.h"
34 typedef struct ALechoState
{
35 DERIVE_FROM_TYPE(ALeffectState
);
37 ALfloat
*SampleBuffer
;
40 // The echo is two tap. The delay is the number of samples from before the
47 /* The panning gains for the two taps */
49 ALfloat Current
[MAX_OUTPUT_CHANNELS
];
50 ALfloat Target
[MAX_OUTPUT_CHANNELS
];
58 static ALvoid
ALechoState_Destruct(ALechoState
*state
);
59 static ALboolean
ALechoState_deviceUpdate(ALechoState
*state
, ALCdevice
*Device
);
60 static ALvoid
ALechoState_update(ALechoState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
);
61 static ALvoid
ALechoState_process(ALechoState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
62 DECLARE_DEFAULT_ALLOCATORS(ALechoState
)
64 DEFINE_ALEFFECTSTATE_VTABLE(ALechoState
);
67 static void ALechoState_Construct(ALechoState
*state
)
69 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
70 SET_VTABLE2(ALechoState
, ALeffectState
, state
);
72 state
->BufferLength
= 0;
73 state
->SampleBuffer
= NULL
;
75 state
->Tap
[0].delay
= 0;
76 state
->Tap
[1].delay
= 0;
79 BiquadState_clear(&state
->Filter
);
82 static ALvoid
ALechoState_Destruct(ALechoState
*state
)
84 al_free(state
->SampleBuffer
);
85 state
->SampleBuffer
= NULL
;
86 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
89 static ALboolean
ALechoState_deviceUpdate(ALechoState
*state
, ALCdevice
*Device
)
93 // Use the next power of 2 for the buffer length, so the tap offsets can be
94 // wrapped using a mask instead of a modulo
95 maxlen
= fastf2i(AL_ECHO_MAX_DELAY
*Device
->Frequency
+ 0.5f
) +
96 fastf2i(AL_ECHO_MAX_LRDELAY
*Device
->Frequency
+ 0.5f
);
97 maxlen
= NextPowerOf2(maxlen
);
98 if(maxlen
<= 0) return AL_FALSE
;
100 if(maxlen
!= state
->BufferLength
)
102 void *temp
= al_calloc(16, maxlen
* sizeof(ALfloat
));
103 if(!temp
) return AL_FALSE
;
105 al_free(state
->SampleBuffer
);
106 state
->SampleBuffer
= temp
;
107 state
->BufferLength
= maxlen
;
110 memset(state
->SampleBuffer
, 0, state
->BufferLength
*sizeof(ALfloat
));
111 memset(state
->Gains
, 0, sizeof(state
->Gains
));
116 static ALvoid
ALechoState_update(ALechoState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
)
118 const ALCdevice
*device
= context
->Device
;
119 ALuint frequency
= device
->Frequency
;
120 ALfloat coeffs
[MAX_AMBI_COEFFS
];
121 ALfloat gainhf
, lrpan
, spread
;
123 state
->Tap
[0].delay
= maxi(fastf2i(props
->Echo
.Delay
*frequency
+ 0.5f
), 1);
124 state
->Tap
[1].delay
= fastf2i(props
->Echo
.LRDelay
*frequency
+ 0.5f
);
125 state
->Tap
[1].delay
+= state
->Tap
[0].delay
;
127 spread
= props
->Echo
.Spread
;
128 if(spread
< 0.0f
) lrpan
= -1.0f
;
130 /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
131 * spread (where 0 = point, tau = omni).
133 spread
= asinf(1.0f
- fabsf(spread
))*4.0f
;
135 state
->FeedGain
= props
->Echo
.Feedback
;
137 gainhf
= maxf(1.0f
- props
->Echo
.Damping
, 0.0625f
); /* Limit -24dB */
138 BiquadState_setParams(&state
->Filter
, BiquadType_HighShelf
,
139 gainhf
, LOWPASSFREQREF
/frequency
,
140 calc_rcpQ_from_slope(gainhf
, 1.0f
));
142 /* First tap panning */
143 CalcAngleCoeffs(-F_PI_2
*lrpan
, 0.0f
, spread
, coeffs
);
144 ComputeDryPanGains(&device
->Dry
, coeffs
, slot
->Params
.Gain
, state
->Gains
[0].Target
);
146 /* Second tap panning */
147 CalcAngleCoeffs( F_PI_2
*lrpan
, 0.0f
, spread
, coeffs
);
148 ComputeDryPanGains(&device
->Dry
, coeffs
, slot
->Params
.Gain
, state
->Gains
[1].Target
);
151 static ALvoid
ALechoState_process(ALechoState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
153 const ALsizei mask
= state
->BufferLength
-1;
154 const ALsizei tap1
= state
->Tap
[0].delay
;
155 const ALsizei tap2
= state
->Tap
[1].delay
;
156 ALfloat
*restrict delaybuf
= state
->SampleBuffer
;
157 ALsizei offset
= state
->Offset
;
158 ALfloat x
[2], y
[2], in
, out
;
162 x
[0] = state
->Filter
.x
[0];
163 x
[1] = state
->Filter
.x
[1];
164 y
[0] = state
->Filter
.y
[0];
165 y
[1] = state
->Filter
.y
[1];
166 for(base
= 0;base
< SamplesToDo
;)
168 alignas(16) ALfloat temps
[2][128];
169 ALsizei td
= mini(128, SamplesToDo
-base
);
171 for(i
= 0;i
< td
;i
++)
173 /* Feed the delay buffer's input first. */
174 delaybuf
[offset
&mask
] = SamplesIn
[0][i
+base
];
177 temps
[0][i
] = delaybuf
[(offset
-tap1
) & mask
];
179 temps
[1][i
] = delaybuf
[(offset
-tap2
) & mask
];
181 /* Apply damping to the second tap, then add it to the buffer with
182 * feedback attenuation.
185 out
= in
*state
->Filter
.b0
+
186 x
[0]*state
->Filter
.b1
+ x
[1]*state
->Filter
.b2
-
187 y
[0]*state
->Filter
.a1
- y
[1]*state
->Filter
.a2
;
188 x
[1] = x
[0]; x
[0] = in
;
189 y
[1] = y
[0]; y
[0] = out
;
191 delaybuf
[offset
&mask
] += out
* state
->FeedGain
;
196 MixSamples(temps
[c
], NumChannels
, SamplesOut
, state
->Gains
[c
].Current
,
197 state
->Gains
[c
].Target
, SamplesToDo
-base
, base
, td
);
201 state
->Filter
.x
[0] = x
[0];
202 state
->Filter
.x
[1] = x
[1];
203 state
->Filter
.y
[0] = y
[0];
204 state
->Filter
.y
[1] = y
[1];
206 state
->Offset
= offset
;
210 typedef struct EchoStateFactory
{
211 DERIVE_FROM_TYPE(EffectStateFactory
);
214 ALeffectState
*EchoStateFactory_create(EchoStateFactory
*UNUSED(factory
))
218 NEW_OBJ0(state
, ALechoState
)();
219 if(!state
) return NULL
;
221 return STATIC_CAST(ALeffectState
, state
);
224 DEFINE_EFFECTSTATEFACTORY_VTABLE(EchoStateFactory
);
226 EffectStateFactory
*EchoStateFactory_getFactory(void)
228 static EchoStateFactory EchoFactory
= { { GET_VTABLE2(EchoStateFactory
, EffectStateFactory
) } };
230 return STATIC_CAST(EffectStateFactory
, &EchoFactory
);
234 void ALecho_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
UNUSED(val
))
235 { alSetError(context
, AL_INVALID_ENUM
, "Invalid echo integer property 0x%04x", param
); }
236 void ALecho_setParamiv(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, const ALint
*UNUSED(vals
))
237 { alSetError(context
, AL_INVALID_ENUM
, "Invalid echo integer-vector property 0x%04x", param
); }
238 void ALecho_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
240 ALeffectProps
*props
= &effect
->Props
;
244 if(!(val
>= AL_ECHO_MIN_DELAY
&& val
<= AL_ECHO_MAX_DELAY
))
245 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo delay out of range");
246 props
->Echo
.Delay
= val
;
249 case AL_ECHO_LRDELAY
:
250 if(!(val
>= AL_ECHO_MIN_LRDELAY
&& val
<= AL_ECHO_MAX_LRDELAY
))
251 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo LR delay out of range");
252 props
->Echo
.LRDelay
= val
;
255 case AL_ECHO_DAMPING
:
256 if(!(val
>= AL_ECHO_MIN_DAMPING
&& val
<= AL_ECHO_MAX_DAMPING
))
257 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo damping out of range");
258 props
->Echo
.Damping
= val
;
261 case AL_ECHO_FEEDBACK
:
262 if(!(val
>= AL_ECHO_MIN_FEEDBACK
&& val
<= AL_ECHO_MAX_FEEDBACK
))
263 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo feedback out of range");
264 props
->Echo
.Feedback
= val
;
268 if(!(val
>= AL_ECHO_MIN_SPREAD
&& val
<= AL_ECHO_MAX_SPREAD
))
269 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo spread out of range");
270 props
->Echo
.Spread
= val
;
274 alSetError(context
, AL_INVALID_ENUM
, "Invalid echo float property 0x%04x", param
);
277 void ALecho_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
278 { ALecho_setParamf(effect
, context
, param
, vals
[0]); }
280 void ALecho_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(val
))
281 { alSetError(context
, AL_INVALID_ENUM
, "Invalid echo integer property 0x%04x", param
); }
282 void ALecho_getParamiv(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(vals
))
283 { alSetError(context
, AL_INVALID_ENUM
, "Invalid echo integer-vector property 0x%04x", param
); }
284 void ALecho_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
286 const ALeffectProps
*props
= &effect
->Props
;
290 *val
= props
->Echo
.Delay
;
293 case AL_ECHO_LRDELAY
:
294 *val
= props
->Echo
.LRDelay
;
297 case AL_ECHO_DAMPING
:
298 *val
= props
->Echo
.Damping
;
301 case AL_ECHO_FEEDBACK
:
302 *val
= props
->Echo
.Feedback
;
306 *val
= props
->Echo
.Spread
;
310 alSetError(context
, AL_INVALID_ENUM
, "Invalid echo float property 0x%04x", param
);
313 void ALecho_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
314 { ALecho_getParamf(effect
, context
, param
, vals
); }
316 DEFINE_ALEFFECT_VTABLE(ALecho
);