2 * OpenAL cross platform audio library
3 * Copyright (C) 2013 by Mike Gorchak
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
27 #include "alAuxEffectSlot.h"
30 #include "filters/defs.h"
33 static_assert(AL_CHORUS_WAVEFORM_SINUSOID
== AL_FLANGER_WAVEFORM_SINUSOID
, "Chorus/Flanger waveform value mismatch");
34 static_assert(AL_CHORUS_WAVEFORM_TRIANGLE
== AL_FLANGER_WAVEFORM_TRIANGLE
, "Chorus/Flanger waveform value mismatch");
41 typedef struct ALchorusState
{
42 DERIVE_FROM_TYPE(ALeffectState
);
44 ALfloat
*SampleBuffer
;
53 /* Gains for left and right sides */
55 ALfloat Current
[MAX_OUTPUT_CHANNELS
];
56 ALfloat Target
[MAX_OUTPUT_CHANNELS
];
59 /* effect parameters */
60 enum WaveForm waveform
;
66 static ALvoid
ALchorusState_Destruct(ALchorusState
*state
);
67 static ALboolean
ALchorusState_deviceUpdate(ALchorusState
*state
, ALCdevice
*Device
);
68 static ALvoid
ALchorusState_update(ALchorusState
*state
, const ALCcontext
*Context
, const ALeffectslot
*Slot
, const ALeffectProps
*props
);
69 static ALvoid
ALchorusState_process(ALchorusState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
70 DECLARE_DEFAULT_ALLOCATORS(ALchorusState
)
72 DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState
);
75 static void ALchorusState_Construct(ALchorusState
*state
)
77 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
78 SET_VTABLE2(ALchorusState
, ALeffectState
, state
);
80 state
->BufferLength
= 0;
81 state
->SampleBuffer
= NULL
;
83 state
->lfo_offset
= 0;
85 state
->waveform
= WF_Triangle
;
88 static ALvoid
ALchorusState_Destruct(ALchorusState
*state
)
90 al_free(state
->SampleBuffer
);
91 state
->SampleBuffer
= NULL
;
93 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
96 static ALboolean
ALchorusState_deviceUpdate(ALchorusState
*state
, ALCdevice
*Device
)
98 const ALfloat max_delay
= maxf(AL_CHORUS_MAX_DELAY
, AL_FLANGER_MAX_DELAY
);
101 maxlen
= NextPowerOf2(float2int(max_delay
*2.0f
*Device
->Frequency
) + 1u);
102 if(maxlen
<= 0) return AL_FALSE
;
104 if(maxlen
!= state
->BufferLength
)
106 void *temp
= al_calloc(16, maxlen
* sizeof(ALfloat
));
107 if(!temp
) return AL_FALSE
;
109 al_free(state
->SampleBuffer
);
110 state
->SampleBuffer
= temp
;
112 state
->BufferLength
= maxlen
;
115 memset(state
->SampleBuffer
, 0, state
->BufferLength
*sizeof(ALfloat
));
116 memset(state
->Gains
, 0, sizeof(state
->Gains
));
121 static ALvoid
ALchorusState_update(ALchorusState
*state
, const ALCcontext
*Context
, const ALeffectslot
*Slot
, const ALeffectProps
*props
)
123 const ALsizei mindelay
= MAX_RESAMPLE_PADDING
<< FRACTIONBITS
;
124 const ALCdevice
*device
= Context
->Device
;
125 ALfloat frequency
= (ALfloat
)device
->Frequency
;
126 ALfloat coeffs
[MAX_AMBI_COEFFS
];
130 switch(props
->Chorus
.Waveform
)
132 case AL_CHORUS_WAVEFORM_TRIANGLE
:
133 state
->waveform
= WF_Triangle
;
135 case AL_CHORUS_WAVEFORM_SINUSOID
:
136 state
->waveform
= WF_Sinusoid
;
140 /* The LFO depth is scaled to be relative to the sample delay. Clamp the
141 * delay and depth to allow enough padding for resampling.
143 state
->delay
= maxi(float2int(props
->Chorus
.Delay
*frequency
*FRACTIONONE
+ 0.5f
),
145 state
->depth
= minf(props
->Chorus
.Depth
* state
->delay
,
146 (ALfloat
)(state
->delay
- mindelay
));
148 state
->feedback
= props
->Chorus
.Feedback
;
150 /* Gains for left and right sides */
151 CalcAngleCoeffs(-F_PI_2
, 0.0f
, 0.0f
, coeffs
);
152 ComputePanGains(&device
->Dry
, coeffs
, Slot
->Params
.Gain
, state
->Gains
[0].Target
);
153 CalcAngleCoeffs( F_PI_2
, 0.0f
, 0.0f
, coeffs
);
154 ComputePanGains(&device
->Dry
, coeffs
, Slot
->Params
.Gain
, state
->Gains
[1].Target
);
156 phase
= props
->Chorus
.Phase
;
157 rate
= props
->Chorus
.Rate
;
160 state
->lfo_offset
= 0;
161 state
->lfo_range
= 1;
162 state
->lfo_scale
= 0.0f
;
167 /* Calculate LFO coefficient (number of samples per cycle). Limit the
168 * max range to avoid overflow when calculating the displacement.
170 ALsizei lfo_range
= float2int(minf(frequency
/rate
+ 0.5f
, (ALfloat
)(INT_MAX
/360 - 180)));
172 state
->lfo_offset
= float2int((ALfloat
)state
->lfo_offset
/state
->lfo_range
*
173 lfo_range
+ 0.5f
) % lfo_range
;
174 state
->lfo_range
= lfo_range
;
175 switch(state
->waveform
)
178 state
->lfo_scale
= 4.0f
/ state
->lfo_range
;
181 state
->lfo_scale
= F_TAU
/ state
->lfo_range
;
185 /* Calculate lfo phase displacement */
186 if(phase
< 0) phase
= 360 + phase
;
187 state
->lfo_disp
= (state
->lfo_range
*phase
+ 180) / 360;
191 static void GetTriangleDelays(ALint
*restrict delays
, ALsizei offset
, const ALsizei lfo_range
,
192 const ALfloat lfo_scale
, const ALfloat depth
, const ALsizei delay
,
196 for(i
= 0;i
< todo
;i
++)
198 delays
[i
] = fastf2i((1.0f
- fabsf(2.0f
- lfo_scale
*offset
)) * depth
) + delay
;
199 offset
= (offset
+1)%lfo_range
;
203 static void GetSinusoidDelays(ALint
*restrict delays
, ALsizei offset
, const ALsizei lfo_range
,
204 const ALfloat lfo_scale
, const ALfloat depth
, const ALsizei delay
,
208 for(i
= 0;i
< todo
;i
++)
210 delays
[i
] = fastf2i(sinf(lfo_scale
*offset
) * depth
) + delay
;
211 offset
= (offset
+1)%lfo_range
;
216 static ALvoid
ALchorusState_process(ALchorusState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
218 const ALsizei bufmask
= state
->BufferLength
-1;
219 const ALfloat feedback
= state
->feedback
;
220 const ALsizei avgdelay
= (state
->delay
+ (FRACTIONONE
>>1)) >> FRACTIONBITS
;
221 ALfloat
*restrict delaybuf
= state
->SampleBuffer
;
222 ALsizei offset
= state
->offset
;
226 for(base
= 0;base
< SamplesToDo
;)
228 const ALsizei todo
= mini(256, SamplesToDo
-base
);
229 ALint moddelays
[2][256];
230 alignas(16) ALfloat temps
[2][256];
232 if(state
->waveform
== WF_Sinusoid
)
234 GetSinusoidDelays(moddelays
[0], state
->lfo_offset
, state
->lfo_range
, state
->lfo_scale
,
235 state
->depth
, state
->delay
, todo
);
236 GetSinusoidDelays(moddelays
[1], (state
->lfo_offset
+state
->lfo_disp
)%state
->lfo_range
,
237 state
->lfo_range
, state
->lfo_scale
, state
->depth
, state
->delay
,
240 else /*if(state->waveform == WF_Triangle)*/
242 GetTriangleDelays(moddelays
[0], state
->lfo_offset
, state
->lfo_range
, state
->lfo_scale
,
243 state
->depth
, state
->delay
, todo
);
244 GetTriangleDelays(moddelays
[1], (state
->lfo_offset
+state
->lfo_disp
)%state
->lfo_range
,
245 state
->lfo_range
, state
->lfo_scale
, state
->depth
, state
->delay
,
248 state
->lfo_offset
= (state
->lfo_offset
+todo
) % state
->lfo_range
;
250 for(i
= 0;i
< todo
;i
++)
255 // Feed the buffer's input first (necessary for delays < 1).
256 delaybuf
[offset
&bufmask
] = SamplesIn
[0][base
+i
];
258 // Tap for the left output.
259 delay
= offset
- (moddelays
[0][i
]>>FRACTIONBITS
);
260 mu
= (moddelays
[0][i
]&FRACTIONMASK
) * (1.0f
/FRACTIONONE
);
261 temps
[0][i
] = cubic(delaybuf
[(delay
+1) & bufmask
], delaybuf
[(delay
) & bufmask
],
262 delaybuf
[(delay
-1) & bufmask
], delaybuf
[(delay
-2) & bufmask
],
265 // Tap for the right output.
266 delay
= offset
- (moddelays
[1][i
]>>FRACTIONBITS
);
267 mu
= (moddelays
[1][i
]&FRACTIONMASK
) * (1.0f
/FRACTIONONE
);
268 temps
[1][i
] = cubic(delaybuf
[(delay
+1) & bufmask
], delaybuf
[(delay
) & bufmask
],
269 delaybuf
[(delay
-1) & bufmask
], delaybuf
[(delay
-2) & bufmask
],
272 // Accumulate feedback from the average delay of the taps.
273 delaybuf
[offset
&bufmask
] += delaybuf
[(offset
-avgdelay
) & bufmask
] * feedback
;
278 MixSamples(temps
[c
], NumChannels
, SamplesOut
, state
->Gains
[c
].Current
,
279 state
->Gains
[c
].Target
, SamplesToDo
-base
, base
, todo
);
284 state
->offset
= offset
;
288 typedef struct ChorusStateFactory
{
289 DERIVE_FROM_TYPE(EffectStateFactory
);
290 } ChorusStateFactory
;
292 static ALeffectState
*ChorusStateFactory_create(ChorusStateFactory
*UNUSED(factory
))
294 ALchorusState
*state
;
296 NEW_OBJ0(state
, ALchorusState
)();
297 if(!state
) return NULL
;
299 return STATIC_CAST(ALeffectState
, state
);
302 DEFINE_EFFECTSTATEFACTORY_VTABLE(ChorusStateFactory
);
305 EffectStateFactory
*ChorusStateFactory_getFactory(void)
307 static ChorusStateFactory ChorusFactory
= { { GET_VTABLE2(ChorusStateFactory
, EffectStateFactory
) } };
309 return STATIC_CAST(EffectStateFactory
, &ChorusFactory
);
313 void ALchorus_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
315 ALeffectProps
*props
= &effect
->Props
;
318 case AL_CHORUS_WAVEFORM
:
319 if(!(val
>= AL_CHORUS_MIN_WAVEFORM
&& val
<= AL_CHORUS_MAX_WAVEFORM
))
320 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid chorus waveform");
321 props
->Chorus
.Waveform
= val
;
324 case AL_CHORUS_PHASE
:
325 if(!(val
>= AL_CHORUS_MIN_PHASE
&& val
<= AL_CHORUS_MAX_PHASE
))
326 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus phase out of range");
327 props
->Chorus
.Phase
= val
;
331 alSetError(context
, AL_INVALID_ENUM
, "Invalid chorus integer property 0x%04x", param
);
334 void ALchorus_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
335 { ALchorus_setParami(effect
, context
, param
, vals
[0]); }
336 void ALchorus_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
338 ALeffectProps
*props
= &effect
->Props
;
342 if(!(val
>= AL_CHORUS_MIN_RATE
&& val
<= AL_CHORUS_MAX_RATE
))
343 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus rate out of range");
344 props
->Chorus
.Rate
= val
;
347 case AL_CHORUS_DEPTH
:
348 if(!(val
>= AL_CHORUS_MIN_DEPTH
&& val
<= AL_CHORUS_MAX_DEPTH
))
349 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus depth out of range");
350 props
->Chorus
.Depth
= val
;
353 case AL_CHORUS_FEEDBACK
:
354 if(!(val
>= AL_CHORUS_MIN_FEEDBACK
&& val
<= AL_CHORUS_MAX_FEEDBACK
))
355 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus feedback out of range");
356 props
->Chorus
.Feedback
= val
;
359 case AL_CHORUS_DELAY
:
360 if(!(val
>= AL_CHORUS_MIN_DELAY
&& val
<= AL_CHORUS_MAX_DELAY
))
361 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus delay out of range");
362 props
->Chorus
.Delay
= val
;
366 alSetError(context
, AL_INVALID_ENUM
, "Invalid chorus float property 0x%04x", param
);
369 void ALchorus_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
370 { ALchorus_setParamf(effect
, context
, param
, vals
[0]); }
372 void ALchorus_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
374 const ALeffectProps
*props
= &effect
->Props
;
377 case AL_CHORUS_WAVEFORM
:
378 *val
= props
->Chorus
.Waveform
;
381 case AL_CHORUS_PHASE
:
382 *val
= props
->Chorus
.Phase
;
386 alSetError(context
, AL_INVALID_ENUM
, "Invalid chorus integer property 0x%04x", param
);
389 void ALchorus_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
390 { ALchorus_getParami(effect
, context
, param
, vals
); }
391 void ALchorus_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
393 const ALeffectProps
*props
= &effect
->Props
;
397 *val
= props
->Chorus
.Rate
;
400 case AL_CHORUS_DEPTH
:
401 *val
= props
->Chorus
.Depth
;
404 case AL_CHORUS_FEEDBACK
:
405 *val
= props
->Chorus
.Feedback
;
408 case AL_CHORUS_DELAY
:
409 *val
= props
->Chorus
.Delay
;
413 alSetError(context
, AL_INVALID_ENUM
, "Invalid chorus float property 0x%04x", param
);
416 void ALchorus_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
417 { ALchorus_getParamf(effect
, context
, param
, vals
); }
419 DEFINE_ALEFFECT_VTABLE(ALchorus
);
422 /* Flanger is basically a chorus with a really short delay. They can both use
423 * the same processing functions, so piggyback flanger on the chorus functions.
425 typedef struct FlangerStateFactory
{
426 DERIVE_FROM_TYPE(EffectStateFactory
);
427 } FlangerStateFactory
;
429 ALeffectState
*FlangerStateFactory_create(FlangerStateFactory
*UNUSED(factory
))
431 ALchorusState
*state
;
433 NEW_OBJ0(state
, ALchorusState
)();
434 if(!state
) return NULL
;
436 return STATIC_CAST(ALeffectState
, state
);
439 DEFINE_EFFECTSTATEFACTORY_VTABLE(FlangerStateFactory
);
441 EffectStateFactory
*FlangerStateFactory_getFactory(void)
443 static FlangerStateFactory FlangerFactory
= { { GET_VTABLE2(FlangerStateFactory
, EffectStateFactory
) } };
445 return STATIC_CAST(EffectStateFactory
, &FlangerFactory
);
449 void ALflanger_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
451 ALeffectProps
*props
= &effect
->Props
;
454 case AL_FLANGER_WAVEFORM
:
455 if(!(val
>= AL_FLANGER_MIN_WAVEFORM
&& val
<= AL_FLANGER_MAX_WAVEFORM
))
456 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid flanger waveform");
457 props
->Chorus
.Waveform
= val
;
460 case AL_FLANGER_PHASE
:
461 if(!(val
>= AL_FLANGER_MIN_PHASE
&& val
<= AL_FLANGER_MAX_PHASE
))
462 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger phase out of range");
463 props
->Chorus
.Phase
= val
;
467 alSetError(context
, AL_INVALID_ENUM
, "Invalid flanger integer property 0x%04x", param
);
470 void ALflanger_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
471 { ALflanger_setParami(effect
, context
, param
, vals
[0]); }
472 void ALflanger_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
474 ALeffectProps
*props
= &effect
->Props
;
477 case AL_FLANGER_RATE
:
478 if(!(val
>= AL_FLANGER_MIN_RATE
&& val
<= AL_FLANGER_MAX_RATE
))
479 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger rate out of range");
480 props
->Chorus
.Rate
= val
;
483 case AL_FLANGER_DEPTH
:
484 if(!(val
>= AL_FLANGER_MIN_DEPTH
&& val
<= AL_FLANGER_MAX_DEPTH
))
485 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger depth out of range");
486 props
->Chorus
.Depth
= val
;
489 case AL_FLANGER_FEEDBACK
:
490 if(!(val
>= AL_FLANGER_MIN_FEEDBACK
&& val
<= AL_FLANGER_MAX_FEEDBACK
))
491 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger feedback out of range");
492 props
->Chorus
.Feedback
= val
;
495 case AL_FLANGER_DELAY
:
496 if(!(val
>= AL_FLANGER_MIN_DELAY
&& val
<= AL_FLANGER_MAX_DELAY
))
497 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger delay out of range");
498 props
->Chorus
.Delay
= val
;
502 alSetError(context
, AL_INVALID_ENUM
, "Invalid flanger float property 0x%04x", param
);
505 void ALflanger_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
506 { ALflanger_setParamf(effect
, context
, param
, vals
[0]); }
508 void ALflanger_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
510 const ALeffectProps
*props
= &effect
->Props
;
513 case AL_FLANGER_WAVEFORM
:
514 *val
= props
->Chorus
.Waveform
;
517 case AL_FLANGER_PHASE
:
518 *val
= props
->Chorus
.Phase
;
522 alSetError(context
, AL_INVALID_ENUM
, "Invalid flanger integer property 0x%04x", param
);
525 void ALflanger_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
526 { ALflanger_getParami(effect
, context
, param
, vals
); }
527 void ALflanger_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
529 const ALeffectProps
*props
= &effect
->Props
;
532 case AL_FLANGER_RATE
:
533 *val
= props
->Chorus
.Rate
;
536 case AL_FLANGER_DEPTH
:
537 *val
= props
->Chorus
.Depth
;
540 case AL_FLANGER_FEEDBACK
:
541 *val
= props
->Chorus
.Feedback
;
544 case AL_FLANGER_DELAY
:
545 *val
= props
->Chorus
.Delay
;
549 alSetError(context
, AL_INVALID_ENUM
, "Invalid flanger float property 0x%04x", param
);
552 void ALflanger_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
553 { ALflanger_getParamf(effect
, context
, param
, vals
); }
555 DEFINE_ALEFFECT_VTABLE(ALflanger
);