Make MAX_RESAMPLER_PADDING specify the total padding
[openal-soft.git] / alc / effects / chorus.cpp
blob59e05be0c04a74fad5c9e6e56268fc501f2f0a47
1 /**
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
21 #include "config.h"
23 #include <algorithm>
24 #include <climits>
25 #include <cmath>
26 #include <cstdlib>
27 #include <iterator>
29 #include "AL/al.h"
30 #include "AL/alc.h"
31 #include "AL/efx.h"
33 #include "al/auxeffectslot.h"
34 #include "alcmain.h"
35 #include "alcontext.h"
36 #include "almalloc.h"
37 #include "alnumeric.h"
38 #include "alspan.h"
39 #include "alu.h"
40 #include "ambidefs.h"
41 #include "effects/base.h"
42 #include "math_defs.h"
43 #include "opthelpers.h"
44 #include "vector.h"
47 namespace {
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");
52 enum class WaveForm {
53 Sinusoid,
54 Triangle
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);
61 ASSUME(todo > 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);
77 ASSUME(todo > 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;
91 ALuint mOffset{0};
93 ALuint mLfoOffset{0};
94 ALuint mLfoRange{1};
95 ALfloat mLfoScale{0.0f};
96 ALuint mLfoDisp{0};
98 /* Gains for left and right sides */
99 struct {
100 ALfloat Current[MAX_OUTPUT_CHANNELS]{};
101 ALfloat Target[MAX_OUTPUT_CHANNELS]{};
102 } mGains[2];
104 /* effect parameters */
105 WaveForm mWaveform{};
106 ALint mDelay{0};
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);
137 return AL_TRUE;
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;
148 break;
149 case AL_CHORUS_WAVEFORM_SINUSOID:
150 mWaveform = WaveForm::Sinusoid;
151 break;
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};
176 if(!(rate > 0.0f))
178 mLfoOffset = 0;
179 mLfoRange = 1;
180 mLfoScale = 0.0f;
181 mLfoDisp = 0;
183 else
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;
192 switch(mWaveform)
194 case WaveForm::Triangle:
195 mLfoScale = 4.0f / static_cast<float>(mLfoRange);
196 break;
197 case WaveForm::Sinusoid:
198 mLfoScale = al::MathDefs<float>::Tau() / static_cast<float>(mLfoRange);
199 break;
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,
225 todo);
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,
232 todo);
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;
258 ++offset;
261 for(ALsizei c{0};c < 2;c++)
262 MixSamples({temps[c], todo}, samplesOut, mGains[c].Current, mGains[c].Target,
263 samplesToDo-base, base);
265 base += todo;
268 mOffset = offset;
272 void Chorus_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
274 switch(param)
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;
280 break;
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;
286 break;
288 default:
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)
296 switch(param)
298 case AL_CHORUS_RATE:
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;
302 break;
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;
308 break;
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;
314 break;
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;
320 break;
322 default:
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)
331 switch(param)
333 case AL_CHORUS_WAVEFORM:
334 *val = props->Chorus.Waveform;
335 break;
337 case AL_CHORUS_PHASE:
338 *val = props->Chorus.Phase;
339 break;
341 default:
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)
349 switch(param)
351 case AL_CHORUS_RATE:
352 *val = props->Chorus.Rate;
353 break;
355 case AL_CHORUS_DEPTH:
356 *val = props->Chorus.Depth;
357 break;
359 case AL_CHORUS_FEEDBACK:
360 *val = props->Chorus.Feedback;
361 break;
363 case AL_CHORUS_DELAY:
364 *val = props->Chorus.Delay;
365 break;
367 default:
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
385 EffectProps props{};
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;
392 return props;
396 void Flanger_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
398 switch(param)
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;
404 break;
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;
410 break;
412 default:
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)
420 switch(param)
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;
426 break;
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;
432 break;
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;
438 break;
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;
444 break;
446 default:
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)
455 switch(param)
457 case AL_FLANGER_WAVEFORM:
458 *val = props->Chorus.Waveform;
459 break;
461 case AL_FLANGER_PHASE:
462 *val = props->Chorus.Phase;
463 break;
465 default:
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)
473 switch(param)
475 case AL_FLANGER_RATE:
476 *val = props->Chorus.Rate;
477 break;
479 case AL_FLANGER_DEPTH:
480 *val = props->Chorus.Depth;
481 break;
483 case AL_FLANGER_FEEDBACK:
484 *val = props->Chorus.Feedback;
485 break;
487 case AL_FLANGER_DELAY:
488 *val = props->Chorus.Delay;
489 break;
491 default:
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
512 EffectProps props{};
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;
519 return props;
522 } // namespace
524 EffectStateFactory *ChorusStateFactory_getFactory()
526 static ChorusStateFactory ChorusFactory{};
527 return &ChorusFactory;
530 EffectStateFactory *FlangerStateFactory_getFactory()
532 static FlangerStateFactory FlangerFactory{};
533 return &FlangerFactory;