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
33 #include "al/auxeffectslot.h"
35 #include "alcontext.h"
37 #include "alnumeric.h"
41 #include "effects/base.h"
42 #include "math_defs.h"
43 #include "opthelpers.h"
49 static_assert(AL_CHORUS_WAVEFORM_SINUSOID
== AL_FLANGER_WAVEFORM_SINUSOID
, "Chorus/Flanger waveform value mismatch");
50 static_assert(AL_CHORUS_WAVEFORM_TRIANGLE
== AL_FLANGER_WAVEFORM_TRIANGLE
, "Chorus/Flanger waveform value mismatch");
57 void GetTriangleDelays(ALuint
*delays
, const ALuint start_offset
, const ALuint lfo_range
,
58 const ALfloat lfo_scale
, const ALfloat depth
, const ALsizei delay
, const size_t todo
)
60 ASSUME(lfo_range
> 0);
63 ALuint offset
{start_offset
};
64 auto gen_lfo
= [&offset
,lfo_range
,lfo_scale
,depth
,delay
]() -> ALuint
66 offset
= (offset
+1)%lfo_range
;
67 return static_cast<ALuint
>(
68 fastf2i((1.0f
- std::abs(2.0f
- lfo_scale
*offset
)) * depth
) + delay
);
70 std::generate_n(delays
, todo
, gen_lfo
);
73 void GetSinusoidDelays(ALuint
*delays
, const ALuint start_offset
, const ALuint lfo_range
,
74 const ALfloat lfo_scale
, const ALfloat depth
, const ALsizei delay
, const size_t todo
)
76 ASSUME(lfo_range
> 0);
79 ALuint offset
{start_offset
};
80 auto gen_lfo
= [&offset
,lfo_range
,lfo_scale
,depth
,delay
]() -> ALuint
82 offset
= (offset
+1)%lfo_range
;
83 return static_cast<ALuint
>(fastf2i(std::sin(lfo_scale
*offset
) * depth
) + delay
);
85 std::generate_n(delays
, todo
, gen_lfo
);
88 struct ChorusState final
: public EffectState
{
89 al::vector
<ALfloat
,16> mSampleBuffer
;
94 ALfloat mLfoScale
{0.0f
};
97 /* Gains for left and right sides */
99 ALfloat Current
[MAX_OUTPUT_CHANNELS
]{};
100 ALfloat Target
[MAX_OUTPUT_CHANNELS
]{};
103 /* effect parameters */
104 WaveForm mWaveform
{};
106 ALfloat mDepth
{0.0f
};
107 ALfloat mFeedback
{0.0f
};
110 ALboolean
deviceUpdate(const ALCdevice
*device
) override
;
111 void update(const ALCcontext
*context
, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
) override
;
112 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
) override
;
114 DEF_NEWDEL(ChorusState
)
117 ALboolean
ChorusState::deviceUpdate(const ALCdevice
*Device
)
119 constexpr ALfloat max_delay
{maxf(AL_CHORUS_MAX_DELAY
, AL_FLANGER_MAX_DELAY
)};
121 const size_t maxlen
{NextPowerOf2(float2uint(max_delay
*2.0f
*Device
->Frequency
) + 1u)};
122 if(maxlen
!= mSampleBuffer
.size())
124 mSampleBuffer
.resize(maxlen
);
125 mSampleBuffer
.shrink_to_fit();
128 std::fill(mSampleBuffer
.begin(), mSampleBuffer
.end(), 0.0f
);
129 for(auto &e
: mGains
)
131 std::fill(std::begin(e
.Current
), std::end(e
.Current
), 0.0f
);
132 std::fill(std::begin(e
.Target
), std::end(e
.Target
), 0.0f
);
138 void ChorusState::update(const ALCcontext
*Context
, const ALeffectslot
*Slot
, const EffectProps
*props
, const EffectTarget target
)
140 constexpr ALsizei mindelay
{MAX_RESAMPLE_PADDING
<< FRACTIONBITS
};
142 switch(props
->Chorus
.Waveform
)
144 case AL_CHORUS_WAVEFORM_TRIANGLE
:
145 mWaveform
= WaveForm::Triangle
;
147 case AL_CHORUS_WAVEFORM_SINUSOID
:
148 mWaveform
= WaveForm::Sinusoid
;
152 /* The LFO depth is scaled to be relative to the sample delay. Clamp the
153 * delay and depth to allow enough padding for resampling.
155 const ALCdevice
*device
{Context
->mDevice
.get()};
156 const auto frequency
= static_cast<ALfloat
>(device
->Frequency
);
157 mDelay
= maxi(float2int(props
->Chorus
.Delay
*frequency
*FRACTIONONE
+ 0.5f
), mindelay
);
158 mDepth
= minf(props
->Chorus
.Depth
* mDelay
, static_cast<ALfloat
>(mDelay
- mindelay
));
160 mFeedback
= props
->Chorus
.Feedback
;
162 /* Gains for left and right sides */
163 ALfloat coeffs
[2][MAX_AMBI_CHANNELS
];
164 CalcDirectionCoeffs({-1.0f
, 0.0f
, 0.0f
}, 0.0f
, coeffs
[0]);
165 CalcDirectionCoeffs({ 1.0f
, 0.0f
, 0.0f
}, 0.0f
, coeffs
[1]);
167 mOutTarget
= target
.Main
->Buffer
;
168 ComputePanGains(target
.Main
, coeffs
[0], Slot
->Params
.Gain
, mGains
[0].Target
);
169 ComputePanGains(target
.Main
, coeffs
[1], Slot
->Params
.Gain
, mGains
[1].Target
);
171 ALfloat rate
{props
->Chorus
.Rate
};
181 /* Calculate LFO coefficient (number of samples per cycle). Limit the
182 * max range to avoid overflow when calculating the displacement.
184 ALuint lfo_range
{float2uint(minf(frequency
/rate
+ 0.5f
, ALfloat
{INT_MAX
/360 - 180}))};
186 mLfoOffset
= mLfoOffset
* lfo_range
/ mLfoRange
;
187 mLfoRange
= lfo_range
;
190 case WaveForm::Triangle
:
191 mLfoScale
= 4.0f
/ mLfoRange
;
193 case WaveForm::Sinusoid
:
194 mLfoScale
= al::MathDefs
<float>::Tau() / mLfoRange
;
198 /* Calculate lfo phase displacement */
199 ALint phase
{props
->Chorus
.Phase
};
200 if(phase
< 0) phase
= 360 + phase
;
201 mLfoDisp
= (mLfoRange
*static_cast<ALuint
>(phase
) + 180) / 360;
205 void ChorusState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
207 const size_t bufmask
{mSampleBuffer
.size()-1};
208 const ALfloat feedback
{mFeedback
};
209 const ALuint avgdelay
{(static_cast<ALuint
>(mDelay
) + (FRACTIONONE
>>1)) >> FRACTIONBITS
};
210 ALfloat
*RESTRICT delaybuf
{mSampleBuffer
.data()};
211 ALuint offset
{mOffset
};
213 for(size_t base
{0u};base
< samplesToDo
;)
215 const size_t todo
{minz(256, samplesToDo
-base
)};
217 ALuint moddelays
[2][256];
218 if(mWaveform
== WaveForm::Sinusoid
)
220 GetSinusoidDelays(moddelays
[0], mLfoOffset
, mLfoRange
, mLfoScale
, mDepth
, mDelay
,
222 GetSinusoidDelays(moddelays
[1], (mLfoOffset
+mLfoDisp
)%mLfoRange
, mLfoRange
, mLfoScale
,
223 mDepth
, mDelay
, todo
);
225 else /*if(mWaveform == WaveForm::Triangle)*/
227 GetTriangleDelays(moddelays
[0], mLfoOffset
, mLfoRange
, mLfoScale
, mDepth
, mDelay
,
229 GetTriangleDelays(moddelays
[1], (mLfoOffset
+mLfoDisp
)%mLfoRange
, mLfoRange
, mLfoScale
,
230 mDepth
, mDelay
, todo
);
232 mLfoOffset
= (mLfoOffset
+todo
) % mLfoRange
;
234 alignas(16) ALfloat temps
[2][256];
235 for(size_t i
{0u};i
< todo
;i
++)
237 // Feed the buffer's input first (necessary for delays < 1).
238 delaybuf
[offset
&bufmask
] = samplesIn
[0][base
+i
];
240 // Tap for the left output.
241 ALuint delay
{offset
- (moddelays
[0][i
]>>FRACTIONBITS
)};
242 ALfloat mu
{(moddelays
[0][i
]&FRACTIONMASK
) * (1.0f
/FRACTIONONE
)};
243 temps
[0][i
] = cubic(delaybuf
[(delay
+1) & bufmask
], delaybuf
[(delay
) & bufmask
],
244 delaybuf
[(delay
-1) & bufmask
], delaybuf
[(delay
-2) & bufmask
],
247 // Tap for the right output.
248 delay
= offset
- (moddelays
[1][i
]>>FRACTIONBITS
);
249 mu
= (moddelays
[1][i
]&FRACTIONMASK
) * (1.0f
/FRACTIONONE
);
250 temps
[1][i
] = cubic(delaybuf
[(delay
+1) & bufmask
], delaybuf
[(delay
) & bufmask
],
251 delaybuf
[(delay
-1) & bufmask
], delaybuf
[(delay
-2) & bufmask
],
254 // Accumulate feedback from the average delay of the taps.
255 delaybuf
[offset
&bufmask
] += delaybuf
[(offset
-avgdelay
) & bufmask
] * feedback
;
259 for(ALsizei c
{0};c
< 2;c
++)
260 MixSamples({temps
[c
], todo
}, samplesOut
, mGains
[c
].Current
, mGains
[c
].Target
,
261 samplesToDo
-base
, base
);
270 void Chorus_setParami(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint val
)
274 case AL_CHORUS_WAVEFORM
:
275 if(!(val
>= AL_CHORUS_MIN_WAVEFORM
&& val
<= AL_CHORUS_MAX_WAVEFORM
))
276 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid chorus waveform");
277 props
->Chorus
.Waveform
= val
;
280 case AL_CHORUS_PHASE
:
281 if(!(val
>= AL_CHORUS_MIN_PHASE
&& val
<= AL_CHORUS_MAX_PHASE
))
282 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus phase out of range");
283 props
->Chorus
.Phase
= val
;
287 context
->setError(AL_INVALID_ENUM
, "Invalid chorus integer property 0x%04x", param
);
290 void Chorus_setParamiv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
291 { Chorus_setParami(props
, context
, param
, vals
[0]); }
292 void Chorus_setParamf(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat val
)
297 if(!(val
>= AL_CHORUS_MIN_RATE
&& val
<= AL_CHORUS_MAX_RATE
))
298 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus rate out of range");
299 props
->Chorus
.Rate
= val
;
302 case AL_CHORUS_DEPTH
:
303 if(!(val
>= AL_CHORUS_MIN_DEPTH
&& val
<= AL_CHORUS_MAX_DEPTH
))
304 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus depth out of range");
305 props
->Chorus
.Depth
= val
;
308 case AL_CHORUS_FEEDBACK
:
309 if(!(val
>= AL_CHORUS_MIN_FEEDBACK
&& val
<= AL_CHORUS_MAX_FEEDBACK
))
310 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus feedback out of range");
311 props
->Chorus
.Feedback
= val
;
314 case AL_CHORUS_DELAY
:
315 if(!(val
>= AL_CHORUS_MIN_DELAY
&& val
<= AL_CHORUS_MAX_DELAY
))
316 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus delay out of range");
317 props
->Chorus
.Delay
= val
;
321 context
->setError(AL_INVALID_ENUM
, "Invalid chorus float property 0x%04x", param
);
324 void Chorus_setParamfv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
325 { Chorus_setParamf(props
, context
, param
, vals
[0]); }
327 void Chorus_getParami(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*val
)
331 case AL_CHORUS_WAVEFORM
:
332 *val
= props
->Chorus
.Waveform
;
335 case AL_CHORUS_PHASE
:
336 *val
= props
->Chorus
.Phase
;
340 context
->setError(AL_INVALID_ENUM
, "Invalid chorus integer property 0x%04x", param
);
343 void Chorus_getParamiv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
344 { Chorus_getParami(props
, context
, param
, vals
); }
345 void Chorus_getParamf(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
350 *val
= props
->Chorus
.Rate
;
353 case AL_CHORUS_DEPTH
:
354 *val
= props
->Chorus
.Depth
;
357 case AL_CHORUS_FEEDBACK
:
358 *val
= props
->Chorus
.Feedback
;
361 case AL_CHORUS_DELAY
:
362 *val
= props
->Chorus
.Delay
;
366 context
->setError(AL_INVALID_ENUM
, "Invalid chorus float property 0x%04x", param
);
369 void Chorus_getParamfv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
370 { Chorus_getParamf(props
, context
, param
, vals
); }
372 DEFINE_ALEFFECT_VTABLE(Chorus
);
375 struct ChorusStateFactory final
: public EffectStateFactory
{
376 EffectState
*create() override
{ return new ChorusState
{}; }
377 EffectProps
getDefaultProps() const noexcept override
;
378 const EffectVtable
*getEffectVtable() const noexcept override
{ return &Chorus_vtable
; }
381 EffectProps
ChorusStateFactory::getDefaultProps() const noexcept
384 props
.Chorus
.Waveform
= AL_CHORUS_DEFAULT_WAVEFORM
;
385 props
.Chorus
.Phase
= AL_CHORUS_DEFAULT_PHASE
;
386 props
.Chorus
.Rate
= AL_CHORUS_DEFAULT_RATE
;
387 props
.Chorus
.Depth
= AL_CHORUS_DEFAULT_DEPTH
;
388 props
.Chorus
.Feedback
= AL_CHORUS_DEFAULT_FEEDBACK
;
389 props
.Chorus
.Delay
= AL_CHORUS_DEFAULT_DELAY
;
394 void Flanger_setParami(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint val
)
398 case AL_FLANGER_WAVEFORM
:
399 if(!(val
>= AL_FLANGER_MIN_WAVEFORM
&& val
<= AL_FLANGER_MAX_WAVEFORM
))
400 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid flanger waveform");
401 props
->Chorus
.Waveform
= val
;
404 case AL_FLANGER_PHASE
:
405 if(!(val
>= AL_FLANGER_MIN_PHASE
&& val
<= AL_FLANGER_MAX_PHASE
))
406 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger phase out of range");
407 props
->Chorus
.Phase
= val
;
411 context
->setError(AL_INVALID_ENUM
, "Invalid flanger integer property 0x%04x", param
);
414 void Flanger_setParamiv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
415 { Flanger_setParami(props
, context
, param
, vals
[0]); }
416 void Flanger_setParamf(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat val
)
420 case AL_FLANGER_RATE
:
421 if(!(val
>= AL_FLANGER_MIN_RATE
&& val
<= AL_FLANGER_MAX_RATE
))
422 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger rate out of range");
423 props
->Chorus
.Rate
= val
;
426 case AL_FLANGER_DEPTH
:
427 if(!(val
>= AL_FLANGER_MIN_DEPTH
&& val
<= AL_FLANGER_MAX_DEPTH
))
428 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger depth out of range");
429 props
->Chorus
.Depth
= val
;
432 case AL_FLANGER_FEEDBACK
:
433 if(!(val
>= AL_FLANGER_MIN_FEEDBACK
&& val
<= AL_FLANGER_MAX_FEEDBACK
))
434 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger feedback out of range");
435 props
->Chorus
.Feedback
= val
;
438 case AL_FLANGER_DELAY
:
439 if(!(val
>= AL_FLANGER_MIN_DELAY
&& val
<= AL_FLANGER_MAX_DELAY
))
440 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger delay out of range");
441 props
->Chorus
.Delay
= val
;
445 context
->setError(AL_INVALID_ENUM
, "Invalid flanger float property 0x%04x", param
);
448 void Flanger_setParamfv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
449 { Flanger_setParamf(props
, context
, param
, vals
[0]); }
451 void Flanger_getParami(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*val
)
455 case AL_FLANGER_WAVEFORM
:
456 *val
= props
->Chorus
.Waveform
;
459 case AL_FLANGER_PHASE
:
460 *val
= props
->Chorus
.Phase
;
464 context
->setError(AL_INVALID_ENUM
, "Invalid flanger integer property 0x%04x", param
);
467 void Flanger_getParamiv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
468 { Flanger_getParami(props
, context
, param
, vals
); }
469 void Flanger_getParamf(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
473 case AL_FLANGER_RATE
:
474 *val
= props
->Chorus
.Rate
;
477 case AL_FLANGER_DEPTH
:
478 *val
= props
->Chorus
.Depth
;
481 case AL_FLANGER_FEEDBACK
:
482 *val
= props
->Chorus
.Feedback
;
485 case AL_FLANGER_DELAY
:
486 *val
= props
->Chorus
.Delay
;
490 context
->setError(AL_INVALID_ENUM
, "Invalid flanger float property 0x%04x", param
);
493 void Flanger_getParamfv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
494 { Flanger_getParamf(props
, context
, param
, vals
); }
496 DEFINE_ALEFFECT_VTABLE(Flanger
);
499 /* Flanger is basically a chorus with a really short delay. They can both use
500 * the same processing functions, so piggyback flanger on the chorus functions.
502 struct FlangerStateFactory final
: public EffectStateFactory
{
503 EffectState
*create() override
{ return new ChorusState
{}; }
504 EffectProps
getDefaultProps() const noexcept override
;
505 const EffectVtable
*getEffectVtable() const noexcept override
{ return &Flanger_vtable
; }
508 EffectProps
FlangerStateFactory::getDefaultProps() const noexcept
511 props
.Chorus
.Waveform
= AL_FLANGER_DEFAULT_WAVEFORM
;
512 props
.Chorus
.Phase
= AL_FLANGER_DEFAULT_PHASE
;
513 props
.Chorus
.Rate
= AL_FLANGER_DEFAULT_RATE
;
514 props
.Chorus
.Depth
= AL_FLANGER_DEFAULT_DEPTH
;
515 props
.Chorus
.Feedback
= AL_FLANGER_DEFAULT_FEEDBACK
;
516 props
.Chorus
.Delay
= AL_FLANGER_DEFAULT_DELAY
;
522 EffectStateFactory
*ChorusStateFactory_getFactory()
524 static ChorusStateFactory ChorusFactory
{};
525 return &ChorusFactory
;
528 EffectStateFactory
*FlangerStateFactory_getFactory()
530 static FlangerStateFactory FlangerFactory
{};
531 return &FlangerFactory
;