Avoid class templates for the POPCNT64/CTZ64 macros
[openal-soft.git] / alc / effects / chorus.cpp
blob418c5d2d28181e9642e53004ebecd81770af9f7d
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 #define MAX_UPDATE_SAMPLES 256
59 struct ChorusState final : public EffectState {
60 al::vector<float,16> mSampleBuffer;
61 ALuint mOffset{0};
63 ALuint mLfoOffset{0};
64 ALuint mLfoRange{1};
65 float mLfoScale{0.0f};
66 ALuint mLfoDisp{0};
68 /* Gains for left and right sides */
69 struct {
70 float Current[MAX_OUTPUT_CHANNELS]{};
71 float Target[MAX_OUTPUT_CHANNELS]{};
72 } mGains[2];
74 /* effect parameters */
75 WaveForm mWaveform{};
76 int mDelay{0};
77 float mDepth{0.0f};
78 float mFeedback{0.0f};
80 void getTriangleDelays(ALuint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo);
81 void getSinusoidDelays(ALuint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo);
83 void deviceUpdate(const ALCdevice *device) override;
84 void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
85 void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override;
87 DEF_NEWDEL(ChorusState)
90 void ChorusState::deviceUpdate(const ALCdevice *Device)
92 constexpr float max_delay{maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY)};
94 const auto frequency = static_cast<float>(Device->Frequency);
95 const size_t maxlen{NextPowerOf2(float2uint(max_delay*2.0f*frequency) + 1u)};
96 if(maxlen != mSampleBuffer.size())
97 al::vector<float,16>(maxlen).swap(mSampleBuffer);
99 std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f);
100 for(auto &e : mGains)
102 std::fill(std::begin(e.Current), std::end(e.Current), 0.0f);
103 std::fill(std::begin(e.Target), std::end(e.Target), 0.0f);
107 void ChorusState::update(const ALCcontext *Context, const ALeffectslot *Slot, const EffectProps *props, const EffectTarget target)
109 constexpr ALsizei mindelay{(MAX_RESAMPLER_PADDING>>1) << FRACTIONBITS};
111 switch(props->Chorus.Waveform)
113 case AL_CHORUS_WAVEFORM_TRIANGLE:
114 mWaveform = WaveForm::Triangle;
115 break;
116 case AL_CHORUS_WAVEFORM_SINUSOID:
117 mWaveform = WaveForm::Sinusoid;
118 break;
121 /* The LFO depth is scaled to be relative to the sample delay. Clamp the
122 * delay and depth to allow enough padding for resampling.
124 const ALCdevice *device{Context->mDevice.get()};
125 const auto frequency = static_cast<float>(device->Frequency);
127 mDelay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f), mindelay);
128 mDepth = minf(props->Chorus.Depth * static_cast<float>(mDelay),
129 static_cast<float>(mDelay - mindelay));
131 mFeedback = props->Chorus.Feedback;
133 /* Gains for left and right sides */
134 const auto lcoeffs = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f}, 0.0f);
135 const auto rcoeffs = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}, 0.0f);
137 mOutTarget = target.Main->Buffer;
138 ComputePanGains(target.Main, lcoeffs.data(), Slot->Params.Gain, mGains[0].Target);
139 ComputePanGains(target.Main, rcoeffs.data(), Slot->Params.Gain, mGains[1].Target);
141 float rate{props->Chorus.Rate};
142 if(!(rate > 0.0f))
144 mLfoOffset = 0;
145 mLfoRange = 1;
146 mLfoScale = 0.0f;
147 mLfoDisp = 0;
149 else
151 /* Calculate LFO coefficient (number of samples per cycle). Limit the
152 * max range to avoid overflow when calculating the displacement.
154 ALuint lfo_range{float2uint(minf(frequency/rate + 0.5f, float{INT_MAX/360 - 180}))};
156 mLfoOffset = mLfoOffset * lfo_range / mLfoRange;
157 mLfoRange = lfo_range;
158 switch(mWaveform)
160 case WaveForm::Triangle:
161 mLfoScale = 4.0f / static_cast<float>(mLfoRange);
162 break;
163 case WaveForm::Sinusoid:
164 mLfoScale = al::MathDefs<float>::Tau() / static_cast<float>(mLfoRange);
165 break;
168 /* Calculate lfo phase displacement */
169 int phase{props->Chorus.Phase};
170 if(phase < 0) phase = 360 + phase;
171 mLfoDisp = (mLfoRange*static_cast<ALuint>(phase) + 180) / 360;
176 void ChorusState::getTriangleDelays(ALuint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo)
178 const ALuint lfo_range{mLfoRange};
179 const float lfo_scale{mLfoScale};
180 const float depth{mDepth};
181 const int delay{mDelay};
183 ASSUME(lfo_range > 0);
184 ASSUME(todo > 0);
186 ALuint offset{mLfoOffset};
187 auto gen_lfo = [&offset,lfo_range,lfo_scale,depth,delay]() -> ALuint
189 offset = (offset+1)%lfo_range;
190 const float offset_norm{static_cast<float>(offset) * lfo_scale};
191 return static_cast<ALuint>(fastf2i((1.0f-std::abs(2.0f-offset_norm)) * depth) + delay);
193 std::generate_n(delays[0], todo, gen_lfo);
195 offset = (mLfoOffset+mLfoDisp) % lfo_range;
196 std::generate_n(delays[1], todo, gen_lfo);
198 mLfoOffset = static_cast<ALuint>(mLfoOffset+todo) % lfo_range;
201 void ChorusState::getSinusoidDelays(ALuint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo)
203 const ALuint lfo_range{mLfoRange};
204 const float lfo_scale{mLfoScale};
205 const float depth{mDepth};
206 const int delay{mDelay};
208 ASSUME(lfo_range > 0);
209 ASSUME(todo > 0);
211 ALuint offset{mLfoOffset};
212 auto gen_lfo = [&offset,lfo_range,lfo_scale,depth,delay]() -> ALuint
214 offset = (offset+1)%lfo_range;
215 const float offset_norm{static_cast<float>(offset) * lfo_scale};
216 return static_cast<ALuint>(fastf2i(std::sin(offset_norm)*depth) + delay);
218 std::generate_n(delays[0], todo, gen_lfo);
220 offset = (mLfoOffset+mLfoDisp) % lfo_range;
221 std::generate_n(delays[1], todo, gen_lfo);
223 mLfoOffset = static_cast<ALuint>(mLfoOffset+todo) % lfo_range;
226 void ChorusState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
228 const size_t bufmask{mSampleBuffer.size()-1};
229 const float feedback{mFeedback};
230 const ALuint avgdelay{(static_cast<ALuint>(mDelay) + (FRACTIONONE>>1)) >> FRACTIONBITS};
231 float *RESTRICT delaybuf{mSampleBuffer.data()};
232 ALuint offset{mOffset};
234 for(size_t base{0u};base < samplesToDo;)
236 const size_t todo{minz(MAX_UPDATE_SAMPLES, samplesToDo-base)};
238 ALuint moddelays[2][MAX_UPDATE_SAMPLES];
239 if(mWaveform == WaveForm::Sinusoid)
240 getSinusoidDelays(moddelays, todo);
241 else /*if(mWaveform == WaveForm::Triangle)*/
242 getTriangleDelays(moddelays, todo);
244 alignas(16) float temps[2][MAX_UPDATE_SAMPLES];
245 for(size_t i{0u};i < todo;++i)
247 // Feed the buffer's input first (necessary for delays < 1).
248 delaybuf[offset&bufmask] = samplesIn[0][base+i];
250 // Tap for the left output.
251 ALuint delay{offset - (moddelays[0][i]>>FRACTIONBITS)};
252 float mu{static_cast<float>(moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE)};
253 temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
254 delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu);
256 // Tap for the right output.
257 delay = offset - (moddelays[1][i]>>FRACTIONBITS);
258 mu = static_cast<float>(moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
259 temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
260 delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu);
262 // Accumulate feedback from the average delay of the taps.
263 delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
264 ++offset;
267 for(ALsizei c{0};c < 2;++c)
268 MixSamples({temps[c], todo}, samplesOut, mGains[c].Current, mGains[c].Target,
269 samplesToDo-base, base);
271 base += todo;
274 mOffset = offset;
278 void Chorus_setParami(EffectProps *props, ALenum param, int val)
280 switch(param)
282 case AL_CHORUS_WAVEFORM:
283 if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
284 throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform"};
285 props->Chorus.Waveform = val;
286 break;
288 case AL_CHORUS_PHASE:
289 if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
290 throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range"};
291 props->Chorus.Phase = val;
292 break;
294 default:
295 throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
298 void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals)
299 { Chorus_setParami(props, param, vals[0]); }
300 void Chorus_setParamf(EffectProps *props, ALenum param, float val)
302 switch(param)
304 case AL_CHORUS_RATE:
305 if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
306 throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range"};
307 props->Chorus.Rate = val;
308 break;
310 case AL_CHORUS_DEPTH:
311 if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
312 throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range"};
313 props->Chorus.Depth = val;
314 break;
316 case AL_CHORUS_FEEDBACK:
317 if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
318 throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range"};
319 props->Chorus.Feedback = val;
320 break;
322 case AL_CHORUS_DELAY:
323 if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
324 throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range"};
325 props->Chorus.Delay = val;
326 break;
328 default:
329 throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
332 void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals)
333 { Chorus_setParamf(props, param, vals[0]); }
335 void Chorus_getParami(const EffectProps *props, ALenum param, int *val)
337 switch(param)
339 case AL_CHORUS_WAVEFORM:
340 *val = props->Chorus.Waveform;
341 break;
343 case AL_CHORUS_PHASE:
344 *val = props->Chorus.Phase;
345 break;
347 default:
348 throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
351 void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals)
352 { Chorus_getParami(props, param, vals); }
353 void Chorus_getParamf(const EffectProps *props, ALenum param, float *val)
355 switch(param)
357 case AL_CHORUS_RATE:
358 *val = props->Chorus.Rate;
359 break;
361 case AL_CHORUS_DEPTH:
362 *val = props->Chorus.Depth;
363 break;
365 case AL_CHORUS_FEEDBACK:
366 *val = props->Chorus.Feedback;
367 break;
369 case AL_CHORUS_DELAY:
370 *val = props->Chorus.Delay;
371 break;
373 default:
374 throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
377 void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals)
378 { Chorus_getParamf(props, param, vals); }
380 DEFINE_ALEFFECT_VTABLE(Chorus);
383 struct ChorusStateFactory final : public EffectStateFactory {
384 EffectState *create() override { return new ChorusState{}; }
385 EffectProps getDefaultProps() const noexcept override;
386 const EffectVtable *getEffectVtable() const noexcept override { return &Chorus_vtable; }
389 EffectProps ChorusStateFactory::getDefaultProps() const noexcept
391 EffectProps props{};
392 props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
393 props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
394 props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
395 props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
396 props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
397 props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
398 return props;
402 void Flanger_setParami(EffectProps *props, ALenum param, int val)
404 switch(param)
406 case AL_FLANGER_WAVEFORM:
407 if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
408 throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform"};
409 props->Chorus.Waveform = val;
410 break;
412 case AL_FLANGER_PHASE:
413 if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
414 throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range"};
415 props->Chorus.Phase = val;
416 break;
418 default:
419 throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
422 void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals)
423 { Flanger_setParami(props, param, vals[0]); }
424 void Flanger_setParamf(EffectProps *props, ALenum param, float val)
426 switch(param)
428 case AL_FLANGER_RATE:
429 if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
430 throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range"};
431 props->Chorus.Rate = val;
432 break;
434 case AL_FLANGER_DEPTH:
435 if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
436 throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range"};
437 props->Chorus.Depth = val;
438 break;
440 case AL_FLANGER_FEEDBACK:
441 if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
442 throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range"};
443 props->Chorus.Feedback = val;
444 break;
446 case AL_FLANGER_DELAY:
447 if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
448 throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range"};
449 props->Chorus.Delay = val;
450 break;
452 default:
453 throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
456 void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals)
457 { Flanger_setParamf(props, param, vals[0]); }
459 void Flanger_getParami(const EffectProps *props, ALenum param, int *val)
461 switch(param)
463 case AL_FLANGER_WAVEFORM:
464 *val = props->Chorus.Waveform;
465 break;
467 case AL_FLANGER_PHASE:
468 *val = props->Chorus.Phase;
469 break;
471 default:
472 throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
475 void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals)
476 { Flanger_getParami(props, param, vals); }
477 void Flanger_getParamf(const EffectProps *props, ALenum param, float *val)
479 switch(param)
481 case AL_FLANGER_RATE:
482 *val = props->Chorus.Rate;
483 break;
485 case AL_FLANGER_DEPTH:
486 *val = props->Chorus.Depth;
487 break;
489 case AL_FLANGER_FEEDBACK:
490 *val = props->Chorus.Feedback;
491 break;
493 case AL_FLANGER_DELAY:
494 *val = props->Chorus.Delay;
495 break;
497 default:
498 throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
501 void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals)
502 { Flanger_getParamf(props, param, vals); }
504 DEFINE_ALEFFECT_VTABLE(Flanger);
507 /* Flanger is basically a chorus with a really short delay. They can both use
508 * the same processing functions, so piggyback flanger on the chorus functions.
510 struct FlangerStateFactory final : public EffectStateFactory {
511 EffectState *create() override { return new ChorusState{}; }
512 EffectProps getDefaultProps() const noexcept override;
513 const EffectVtable *getEffectVtable() const noexcept override { return &Flanger_vtable; }
516 EffectProps FlangerStateFactory::getDefaultProps() const noexcept
518 EffectProps props{};
519 props.Chorus.Waveform = AL_FLANGER_DEFAULT_WAVEFORM;
520 props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
521 props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
522 props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
523 props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
524 props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
525 return props;
528 } // namespace
530 EffectStateFactory *ChorusStateFactory_getFactory()
532 static ChorusStateFactory ChorusFactory{};
533 return &ChorusFactory;
536 EffectStateFactory *FlangerStateFactory_getFactory()
538 static FlangerStateFactory FlangerFactory{};
539 return &FlangerFactory;