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
46 /* The panning gains for the two taps */
48 ALfloat Current
[MAX_OUTPUT_CHANNELS
];
49 ALfloat Target
[MAX_OUTPUT_CHANNELS
];
57 static ALvoid
ALechoState_Destruct(ALechoState
*state
);
58 static ALboolean
ALechoState_deviceUpdate(ALechoState
*state
, ALCdevice
*Device
);
59 static ALvoid
ALechoState_update(ALechoState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
);
60 static ALvoid
ALechoState_process(ALechoState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
61 DECLARE_DEFAULT_ALLOCATORS(ALechoState
)
63 DEFINE_ALEFFECTSTATE_VTABLE(ALechoState
);
66 static void ALechoState_Construct(ALechoState
*state
)
68 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
69 SET_VTABLE2(ALechoState
, ALeffectState
, state
);
71 state
->BufferLength
= 0;
72 state
->SampleBuffer
= NULL
;
74 state
->Tap
[0].delay
= 0;
75 state
->Tap
[1].delay
= 0;
78 ALfilterState_clear(&state
->Filter
);
81 static ALvoid
ALechoState_Destruct(ALechoState
*state
)
83 al_free(state
->SampleBuffer
);
84 state
->SampleBuffer
= NULL
;
85 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
88 static ALboolean
ALechoState_deviceUpdate(ALechoState
*state
, ALCdevice
*Device
)
92 // Use the next power of 2 for the buffer length, so the tap offsets can be
93 // wrapped using a mask instead of a modulo
94 maxlen
= fastf2i(AL_ECHO_MAX_DELAY
*Device
->Frequency
+ 0.5f
) +
95 fastf2i(AL_ECHO_MAX_LRDELAY
*Device
->Frequency
+ 0.5f
);
96 maxlen
= NextPowerOf2(maxlen
);
97 if(maxlen
<= 0) return AL_FALSE
;
99 if(maxlen
!= state
->BufferLength
)
101 void *temp
= al_calloc(16, maxlen
* sizeof(ALfloat
));
102 if(!temp
) return AL_FALSE
;
104 al_free(state
->SampleBuffer
);
105 state
->SampleBuffer
= temp
;
106 state
->BufferLength
= maxlen
;
109 memset(state
->SampleBuffer
, 0, state
->BufferLength
*sizeof(ALfloat
));
110 memset(state
->Gains
, 0, sizeof(state
->Gains
));
115 static ALvoid
ALechoState_update(ALechoState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
)
117 const ALCdevice
*device
= context
->Device
;
118 ALuint frequency
= device
->Frequency
;
119 ALfloat coeffs
[MAX_AMBI_COEFFS
];
120 ALfloat gainhf
, lrpan
, spread
;
122 state
->Tap
[0].delay
= maxi(fastf2i(props
->Echo
.Delay
*frequency
+ 0.5f
), 1);
123 state
->Tap
[1].delay
= fastf2i(props
->Echo
.LRDelay
*frequency
+ 0.5f
);
124 state
->Tap
[1].delay
+= state
->Tap
[0].delay
;
126 spread
= props
->Echo
.Spread
;
127 if(spread
< 0.0f
) lrpan
= -1.0f
;
129 /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
130 * spread (where 0 = point, tau = omni).
132 spread
= asinf(1.0f
- fabsf(spread
))*4.0f
;
134 state
->FeedGain
= props
->Echo
.Feedback
;
136 gainhf
= maxf(1.0f
- props
->Echo
.Damping
, 0.0625f
); /* Limit -24dB */
137 ALfilterState_setParams(&state
->Filter
, ALfilterType_HighShelf
,
138 gainhf
, LOWPASSFREQREF
/frequency
,
139 calc_rcpQ_from_slope(gainhf
, 1.0f
));
141 /* First tap panning */
142 CalcAngleCoeffs(-F_PI_2
*lrpan
, 0.0f
, spread
, coeffs
);
143 ComputeDryPanGains(&device
->Dry
, coeffs
, slot
->Params
.Gain
, state
->Gains
[0].Target
);
145 /* Second tap panning */
146 CalcAngleCoeffs( F_PI_2
*lrpan
, 0.0f
, spread
, coeffs
);
147 ComputeDryPanGains(&device
->Dry
, coeffs
, slot
->Params
.Gain
, state
->Gains
[1].Target
);
150 static ALvoid
ALechoState_process(ALechoState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
152 const ALsizei mask
= state
->BufferLength
-1;
153 const ALsizei tap1
= state
->Tap
[0].delay
;
154 const ALsizei tap2
= state
->Tap
[1].delay
;
155 ALfloat
*restrict delaybuf
= state
->SampleBuffer
;
156 ALsizei offset
= state
->Offset
;
157 ALfloat x
[2], y
[2], in
, out
;
161 x
[0] = state
->Filter
.x
[0];
162 x
[1] = state
->Filter
.x
[1];
163 y
[0] = state
->Filter
.y
[0];
164 y
[1] = state
->Filter
.y
[1];
165 for(base
= 0;base
< SamplesToDo
;)
167 ALfloat temps
[2][128];
168 ALsizei td
= mini(128, SamplesToDo
-base
);
170 for(i
= 0;i
< td
;i
++)
172 /* Feed the delay buffer's input first. */
173 delaybuf
[offset
&mask
] = SamplesIn
[0][i
+base
];
176 temps
[0][i
] = delaybuf
[(offset
-tap1
) & mask
];
178 temps
[1][i
] = delaybuf
[(offset
-tap2
) & mask
];
180 /* Apply damping to the second tap, then add it to the buffer with
181 * feedback attenuation.
184 out
= in
*state
->Filter
.b0
+
185 x
[0]*state
->Filter
.b1
+ x
[1]*state
->Filter
.b2
-
186 y
[0]*state
->Filter
.a1
- y
[1]*state
->Filter
.a2
;
187 x
[1] = x
[0]; x
[0] = in
;
188 y
[1] = y
[0]; y
[0] = out
;
190 delaybuf
[offset
&mask
] += out
* state
->FeedGain
;
195 MixSamples(temps
[c
], NumChannels
, SamplesOut
, state
->Gains
[c
].Current
,
196 state
->Gains
[c
].Target
, SamplesToDo
-base
, base
, td
);
200 state
->Filter
.x
[0] = x
[0];
201 state
->Filter
.x
[1] = x
[1];
202 state
->Filter
.y
[0] = y
[0];
203 state
->Filter
.y
[1] = y
[1];
205 state
->Offset
= offset
;
209 typedef struct ALechoStateFactory
{
210 DERIVE_FROM_TYPE(ALeffectStateFactory
);
211 } ALechoStateFactory
;
213 ALeffectState
*ALechoStateFactory_create(ALechoStateFactory
*UNUSED(factory
))
217 NEW_OBJ0(state
, ALechoState
)();
218 if(!state
) return NULL
;
220 return STATIC_CAST(ALeffectState
, state
);
223 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory
);
225 ALeffectStateFactory
*ALechoStateFactory_getFactory(void)
227 static ALechoStateFactory EchoFactory
= { { GET_VTABLE2(ALechoStateFactory
, ALeffectStateFactory
) } };
229 return STATIC_CAST(ALeffectStateFactory
, &EchoFactory
);
233 void ALecho_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
UNUSED(val
))
234 { alSetError(context
, AL_INVALID_ENUM
, "Invalid echo integer property 0x%04x", param
); }
235 void ALecho_setParamiv(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, const ALint
*UNUSED(vals
))
236 { alSetError(context
, AL_INVALID_ENUM
, "Invalid echo integer-vector property 0x%04x", param
); }
237 void ALecho_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
239 ALeffectProps
*props
= &effect
->Props
;
243 if(!(val
>= AL_ECHO_MIN_DELAY
&& val
<= AL_ECHO_MAX_DELAY
))
244 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo delay out of range");
245 props
->Echo
.Delay
= val
;
248 case AL_ECHO_LRDELAY
:
249 if(!(val
>= AL_ECHO_MIN_LRDELAY
&& val
<= AL_ECHO_MAX_LRDELAY
))
250 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo LR delay out of range");
251 props
->Echo
.LRDelay
= val
;
254 case AL_ECHO_DAMPING
:
255 if(!(val
>= AL_ECHO_MIN_DAMPING
&& val
<= AL_ECHO_MAX_DAMPING
))
256 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo damping out of range");
257 props
->Echo
.Damping
= val
;
260 case AL_ECHO_FEEDBACK
:
261 if(!(val
>= AL_ECHO_MIN_FEEDBACK
&& val
<= AL_ECHO_MAX_FEEDBACK
))
262 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo feedback out of range");
263 props
->Echo
.Feedback
= val
;
267 if(!(val
>= AL_ECHO_MIN_SPREAD
&& val
<= AL_ECHO_MAX_SPREAD
))
268 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Echo spread out of range");
269 props
->Echo
.Spread
= val
;
273 alSetError(context
, AL_INVALID_ENUM
, "Invalid echo float property 0x%04x", param
);
276 void ALecho_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
277 { ALecho_setParamf(effect
, context
, param
, vals
[0]); }
279 void ALecho_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(val
))
280 { alSetError(context
, AL_INVALID_ENUM
, "Invalid echo integer property 0x%04x", param
); }
281 void ALecho_getParamiv(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(vals
))
282 { alSetError(context
, AL_INVALID_ENUM
, "Invalid echo integer-vector property 0x%04x", param
); }
283 void ALecho_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
285 const ALeffectProps
*props
= &effect
->Props
;
289 *val
= props
->Echo
.Delay
;
292 case AL_ECHO_LRDELAY
:
293 *val
= props
->Echo
.LRDelay
;
296 case AL_ECHO_DAMPING
:
297 *val
= props
->Echo
.Damping
;
300 case AL_ECHO_FEEDBACK
:
301 *val
= props
->Echo
.Feedback
;
305 *val
= props
->Echo
.Spread
;
309 alSetError(context
, AL_INVALID_ENUM
, "Invalid echo float property 0x%04x", param
);
312 void ALecho_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
313 { ALecho_getParamf(effect
, context
, param
, vals
); }
315 DEFINE_ALEFFECT_VTABLE(ALecho
);