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 const float offset_norm
{static_cast<float>(offset
) * lfo_scale
};
68 return static_cast<ALuint
>(fastf2i((1.0f
-std::abs(2.0f
-offset_norm
)) * 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 const float offset_norm
{static_cast<float>(offset
) * lfo_scale
};
84 return static_cast<ALuint
>(fastf2i(std::sin(offset_norm
)*depth
) + delay
);
86 std::generate_n(delays
, todo
, gen_lfo
);
89 struct ChorusState final
: public EffectState
{
90 al::vector
<ALfloat
,16> mSampleBuffer
;
95 ALfloat mLfoScale
{0.0f
};
98 /* Gains for left and right sides */
100 ALfloat Current
[MAX_OUTPUT_CHANNELS
]{};
101 ALfloat Target
[MAX_OUTPUT_CHANNELS
]{};
104 /* effect parameters */
105 WaveForm mWaveform
{};
107 ALfloat mDepth
{0.0f
};
108 ALfloat mFeedback
{0.0f
};
111 ALboolean
deviceUpdate(const ALCdevice
*device
) override
;
112 void update(const ALCcontext
*context
, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
) override
;
113 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
) override
;
115 DEF_NEWDEL(ChorusState
)
118 ALboolean
ChorusState::deviceUpdate(const ALCdevice
*Device
)
120 constexpr ALfloat max_delay
{maxf(AL_CHORUS_MAX_DELAY
, AL_FLANGER_MAX_DELAY
)};
122 const auto frequency
= static_cast<float>(Device
->Frequency
);
123 const size_t maxlen
{NextPowerOf2(float2uint(max_delay
*2.0f
*frequency
) + 1u)};
124 if(maxlen
!= mSampleBuffer
.size())
126 mSampleBuffer
.resize(maxlen
);
127 mSampleBuffer
.shrink_to_fit();
130 std::fill(mSampleBuffer
.begin(), mSampleBuffer
.end(), 0.0f
);
131 for(auto &e
: mGains
)
133 std::fill(std::begin(e
.Current
), std::end(e
.Current
), 0.0f
);
134 std::fill(std::begin(e
.Target
), std::end(e
.Target
), 0.0f
);
140 void ChorusState::update(const ALCcontext
*Context
, const ALeffectslot
*Slot
, const EffectProps
*props
, const EffectTarget target
)
142 constexpr ALsizei mindelay
{(MAX_RESAMPLER_PADDING
>>1) << FRACTIONBITS
};
144 switch(props
->Chorus
.Waveform
)
146 case AL_CHORUS_WAVEFORM_TRIANGLE
:
147 mWaveform
= WaveForm::Triangle
;
149 case AL_CHORUS_WAVEFORM_SINUSOID
:
150 mWaveform
= WaveForm::Sinusoid
;
154 /* The LFO depth is scaled to be relative to the sample delay. Clamp the
155 * delay and depth to allow enough padding for resampling.
157 const ALCdevice
*device
{Context
->mDevice
.get()};
158 const auto frequency
= static_cast<float>(device
->Frequency
);
160 mDelay
= maxi(float2int(props
->Chorus
.Delay
*frequency
*FRACTIONONE
+ 0.5f
), mindelay
);
161 mDepth
= minf(props
->Chorus
.Depth
* static_cast<float>(mDelay
),
162 static_cast<float>(mDelay
- mindelay
));
164 mFeedback
= props
->Chorus
.Feedback
;
166 /* Gains for left and right sides */
167 ALfloat coeffs
[2][MAX_AMBI_CHANNELS
];
168 CalcDirectionCoeffs({-1.0f
, 0.0f
, 0.0f
}, 0.0f
, coeffs
[0]);
169 CalcDirectionCoeffs({ 1.0f
, 0.0f
, 0.0f
}, 0.0f
, coeffs
[1]);
171 mOutTarget
= target
.Main
->Buffer
;
172 ComputePanGains(target
.Main
, coeffs
[0], Slot
->Params
.Gain
, mGains
[0].Target
);
173 ComputePanGains(target
.Main
, coeffs
[1], Slot
->Params
.Gain
, mGains
[1].Target
);
175 ALfloat rate
{props
->Chorus
.Rate
};
185 /* Calculate LFO coefficient (number of samples per cycle). Limit the
186 * max range to avoid overflow when calculating the displacement.
188 ALuint lfo_range
{float2uint(minf(frequency
/rate
+ 0.5f
, ALfloat
{INT_MAX
/360 - 180}))};
190 mLfoOffset
= mLfoOffset
* lfo_range
/ mLfoRange
;
191 mLfoRange
= lfo_range
;
194 case WaveForm::Triangle
:
195 mLfoScale
= 4.0f
/ static_cast<float>(mLfoRange
);
197 case WaveForm::Sinusoid
:
198 mLfoScale
= al::MathDefs
<float>::Tau() / static_cast<float>(mLfoRange
);
202 /* Calculate lfo phase displacement */
203 ALint phase
{props
->Chorus
.Phase
};
204 if(phase
< 0) phase
= 360 + phase
;
205 mLfoDisp
= (mLfoRange
*static_cast<ALuint
>(phase
) + 180) / 360;
209 void ChorusState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
211 const size_t bufmask
{mSampleBuffer
.size()-1};
212 const ALfloat feedback
{mFeedback
};
213 const ALuint avgdelay
{(static_cast<ALuint
>(mDelay
) + (FRACTIONONE
>>1)) >> FRACTIONBITS
};
214 ALfloat
*RESTRICT delaybuf
{mSampleBuffer
.data()};
215 ALuint offset
{mOffset
};
217 for(size_t base
{0u};base
< samplesToDo
;)
219 const size_t todo
{minz(256, samplesToDo
-base
)};
221 ALuint moddelays
[2][256];
222 if(mWaveform
== WaveForm::Sinusoid
)
224 GetSinusoidDelays(moddelays
[0], mLfoOffset
, mLfoRange
, mLfoScale
, mDepth
, mDelay
,
226 GetSinusoidDelays(moddelays
[1], (mLfoOffset
+mLfoDisp
)%mLfoRange
, mLfoRange
, mLfoScale
,
227 mDepth
, mDelay
, todo
);
229 else /*if(mWaveform == WaveForm::Triangle)*/
231 GetTriangleDelays(moddelays
[0], mLfoOffset
, mLfoRange
, mLfoScale
, mDepth
, mDelay
,
233 GetTriangleDelays(moddelays
[1], (mLfoOffset
+mLfoDisp
)%mLfoRange
, mLfoRange
, mLfoScale
,
234 mDepth
, mDelay
, todo
);
236 mLfoOffset
= (mLfoOffset
+static_cast<ALuint
>(todo
)) % mLfoRange
;
238 alignas(16) ALfloat temps
[2][256];
239 for(size_t i
{0u};i
< todo
;i
++)
241 // Feed the buffer's input first (necessary for delays < 1).
242 delaybuf
[offset
&bufmask
] = samplesIn
[0][base
+i
];
244 // Tap for the left output.
245 ALuint delay
{offset
- (moddelays
[0][i
]>>FRACTIONBITS
)};
246 ALfloat mu
{static_cast<float>(moddelays
[0][i
]&FRACTIONMASK
) * (1.0f
/FRACTIONONE
)};
247 temps
[0][i
] = cubic(delaybuf
[(delay
+1) & bufmask
], delaybuf
[(delay
) & bufmask
],
248 delaybuf
[(delay
-1) & bufmask
], delaybuf
[(delay
-2) & bufmask
], mu
);
250 // Tap for the right output.
251 delay
= offset
- (moddelays
[1][i
]>>FRACTIONBITS
);
252 mu
= static_cast<float>(moddelays
[1][i
]&FRACTIONMASK
) * (1.0f
/FRACTIONONE
);
253 temps
[1][i
] = cubic(delaybuf
[(delay
+1) & bufmask
], delaybuf
[(delay
) & bufmask
],
254 delaybuf
[(delay
-1) & bufmask
], delaybuf
[(delay
-2) & bufmask
], mu
);
256 // Accumulate feedback from the average delay of the taps.
257 delaybuf
[offset
&bufmask
] += delaybuf
[(offset
-avgdelay
) & bufmask
] * feedback
;
261 for(ALsizei c
{0};c
< 2;c
++)
262 MixSamples({temps
[c
], todo
}, samplesOut
, mGains
[c
].Current
, mGains
[c
].Target
,
263 samplesToDo
-base
, base
);
272 void Chorus_setParami(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint val
)
276 case AL_CHORUS_WAVEFORM
:
277 if(!(val
>= AL_CHORUS_MIN_WAVEFORM
&& val
<= AL_CHORUS_MAX_WAVEFORM
))
278 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid chorus waveform");
279 props
->Chorus
.Waveform
= val
;
282 case AL_CHORUS_PHASE
:
283 if(!(val
>= AL_CHORUS_MIN_PHASE
&& val
<= AL_CHORUS_MAX_PHASE
))
284 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus phase out of range");
285 props
->Chorus
.Phase
= val
;
289 context
->setError(AL_INVALID_ENUM
, "Invalid chorus integer property 0x%04x", param
);
292 void Chorus_setParamiv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
293 { Chorus_setParami(props
, context
, param
, vals
[0]); }
294 void Chorus_setParamf(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat val
)
299 if(!(val
>= AL_CHORUS_MIN_RATE
&& val
<= AL_CHORUS_MAX_RATE
))
300 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus rate out of range");
301 props
->Chorus
.Rate
= val
;
304 case AL_CHORUS_DEPTH
:
305 if(!(val
>= AL_CHORUS_MIN_DEPTH
&& val
<= AL_CHORUS_MAX_DEPTH
))
306 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus depth out of range");
307 props
->Chorus
.Depth
= val
;
310 case AL_CHORUS_FEEDBACK
:
311 if(!(val
>= AL_CHORUS_MIN_FEEDBACK
&& val
<= AL_CHORUS_MAX_FEEDBACK
))
312 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus feedback out of range");
313 props
->Chorus
.Feedback
= val
;
316 case AL_CHORUS_DELAY
:
317 if(!(val
>= AL_CHORUS_MIN_DELAY
&& val
<= AL_CHORUS_MAX_DELAY
))
318 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Chorus delay out of range");
319 props
->Chorus
.Delay
= val
;
323 context
->setError(AL_INVALID_ENUM
, "Invalid chorus float property 0x%04x", param
);
326 void Chorus_setParamfv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
327 { Chorus_setParamf(props
, context
, param
, vals
[0]); }
329 void Chorus_getParami(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*val
)
333 case AL_CHORUS_WAVEFORM
:
334 *val
= props
->Chorus
.Waveform
;
337 case AL_CHORUS_PHASE
:
338 *val
= props
->Chorus
.Phase
;
342 context
->setError(AL_INVALID_ENUM
, "Invalid chorus integer property 0x%04x", param
);
345 void Chorus_getParamiv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
346 { Chorus_getParami(props
, context
, param
, vals
); }
347 void Chorus_getParamf(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
352 *val
= props
->Chorus
.Rate
;
355 case AL_CHORUS_DEPTH
:
356 *val
= props
->Chorus
.Depth
;
359 case AL_CHORUS_FEEDBACK
:
360 *val
= props
->Chorus
.Feedback
;
363 case AL_CHORUS_DELAY
:
364 *val
= props
->Chorus
.Delay
;
368 context
->setError(AL_INVALID_ENUM
, "Invalid chorus float property 0x%04x", param
);
371 void Chorus_getParamfv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
372 { Chorus_getParamf(props
, context
, param
, vals
); }
374 DEFINE_ALEFFECT_VTABLE(Chorus
);
377 struct ChorusStateFactory final
: public EffectStateFactory
{
378 EffectState
*create() override
{ return new ChorusState
{}; }
379 EffectProps
getDefaultProps() const noexcept override
;
380 const EffectVtable
*getEffectVtable() const noexcept override
{ return &Chorus_vtable
; }
383 EffectProps
ChorusStateFactory::getDefaultProps() const noexcept
386 props
.Chorus
.Waveform
= AL_CHORUS_DEFAULT_WAVEFORM
;
387 props
.Chorus
.Phase
= AL_CHORUS_DEFAULT_PHASE
;
388 props
.Chorus
.Rate
= AL_CHORUS_DEFAULT_RATE
;
389 props
.Chorus
.Depth
= AL_CHORUS_DEFAULT_DEPTH
;
390 props
.Chorus
.Feedback
= AL_CHORUS_DEFAULT_FEEDBACK
;
391 props
.Chorus
.Delay
= AL_CHORUS_DEFAULT_DELAY
;
396 void Flanger_setParami(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint val
)
400 case AL_FLANGER_WAVEFORM
:
401 if(!(val
>= AL_FLANGER_MIN_WAVEFORM
&& val
<= AL_FLANGER_MAX_WAVEFORM
))
402 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid flanger waveform");
403 props
->Chorus
.Waveform
= val
;
406 case AL_FLANGER_PHASE
:
407 if(!(val
>= AL_FLANGER_MIN_PHASE
&& val
<= AL_FLANGER_MAX_PHASE
))
408 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger phase out of range");
409 props
->Chorus
.Phase
= val
;
413 context
->setError(AL_INVALID_ENUM
, "Invalid flanger integer property 0x%04x", param
);
416 void Flanger_setParamiv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
417 { Flanger_setParami(props
, context
, param
, vals
[0]); }
418 void Flanger_setParamf(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat val
)
422 case AL_FLANGER_RATE
:
423 if(!(val
>= AL_FLANGER_MIN_RATE
&& val
<= AL_FLANGER_MAX_RATE
))
424 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger rate out of range");
425 props
->Chorus
.Rate
= val
;
428 case AL_FLANGER_DEPTH
:
429 if(!(val
>= AL_FLANGER_MIN_DEPTH
&& val
<= AL_FLANGER_MAX_DEPTH
))
430 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger depth out of range");
431 props
->Chorus
.Depth
= val
;
434 case AL_FLANGER_FEEDBACK
:
435 if(!(val
>= AL_FLANGER_MIN_FEEDBACK
&& val
<= AL_FLANGER_MAX_FEEDBACK
))
436 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger feedback out of range");
437 props
->Chorus
.Feedback
= val
;
440 case AL_FLANGER_DELAY
:
441 if(!(val
>= AL_FLANGER_MIN_DELAY
&& val
<= AL_FLANGER_MAX_DELAY
))
442 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Flanger delay out of range");
443 props
->Chorus
.Delay
= val
;
447 context
->setError(AL_INVALID_ENUM
, "Invalid flanger float property 0x%04x", param
);
450 void Flanger_setParamfv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
451 { Flanger_setParamf(props
, context
, param
, vals
[0]); }
453 void Flanger_getParami(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*val
)
457 case AL_FLANGER_WAVEFORM
:
458 *val
= props
->Chorus
.Waveform
;
461 case AL_FLANGER_PHASE
:
462 *val
= props
->Chorus
.Phase
;
466 context
->setError(AL_INVALID_ENUM
, "Invalid flanger integer property 0x%04x", param
);
469 void Flanger_getParamiv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
470 { Flanger_getParami(props
, context
, param
, vals
); }
471 void Flanger_getParamf(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
475 case AL_FLANGER_RATE
:
476 *val
= props
->Chorus
.Rate
;
479 case AL_FLANGER_DEPTH
:
480 *val
= props
->Chorus
.Depth
;
483 case AL_FLANGER_FEEDBACK
:
484 *val
= props
->Chorus
.Feedback
;
487 case AL_FLANGER_DELAY
:
488 *val
= props
->Chorus
.Delay
;
492 context
->setError(AL_INVALID_ENUM
, "Invalid flanger float property 0x%04x", param
);
495 void Flanger_getParamfv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
496 { Flanger_getParamf(props
, context
, param
, vals
); }
498 DEFINE_ALEFFECT_VTABLE(Flanger
);
501 /* Flanger is basically a chorus with a really short delay. They can both use
502 * the same processing functions, so piggyback flanger on the chorus functions.
504 struct FlangerStateFactory final
: public EffectStateFactory
{
505 EffectState
*create() override
{ return new ChorusState
{}; }
506 EffectProps
getDefaultProps() const noexcept override
;
507 const EffectVtable
*getEffectVtable() const noexcept override
{ return &Flanger_vtable
; }
510 EffectProps
FlangerStateFactory::getDefaultProps() const noexcept
513 props
.Chorus
.Waveform
= AL_FLANGER_DEFAULT_WAVEFORM
;
514 props
.Chorus
.Phase
= AL_FLANGER_DEFAULT_PHASE
;
515 props
.Chorus
.Rate
= AL_FLANGER_DEFAULT_RATE
;
516 props
.Chorus
.Depth
= AL_FLANGER_DEFAULT_DEPTH
;
517 props
.Chorus
.Feedback
= AL_FLANGER_DEFAULT_FEEDBACK
;
518 props
.Chorus
.Delay
= AL_FLANGER_DEFAULT_DELAY
;
524 EffectStateFactory
*ChorusStateFactory_getFactory()
526 static ChorusStateFactory ChorusFactory
{};
527 return &ChorusFactory
;
530 EffectStateFactory
*FlangerStateFactory_getFactory()
532 static FlangerStateFactory FlangerFactory
{};
533 return &FlangerFactory
;