Merge pull request #342 from Lopuska/patch-1
[openal-soft.git] / alc / effects / chorus.cpp
blob6e73f1f09fbd3f910c074b38bf4f1f1484dd958b
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 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);
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 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;
90 ALuint mOffset{0};
92 ALuint mLfoOffset{0};
93 ALuint mLfoRange{1};
94 ALfloat mLfoScale{0.0f};
95 ALuint mLfoDisp{0};
97 /* Gains for left and right sides */
98 struct {
99 ALfloat Current[MAX_OUTPUT_CHANNELS]{};
100 ALfloat Target[MAX_OUTPUT_CHANNELS]{};
101 } mGains[2];
103 /* effect parameters */
104 WaveForm mWaveform{};
105 ALint mDelay{0};
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);
135 return AL_TRUE;
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;
146 break;
147 case AL_CHORUS_WAVEFORM_SINUSOID:
148 mWaveform = WaveForm::Sinusoid;
149 break;
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};
172 if(!(rate > 0.0f))
174 mLfoOffset = 0;
175 mLfoRange = 1;
176 mLfoScale = 0.0f;
177 mLfoDisp = 0;
179 else
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;
188 switch(mWaveform)
190 case WaveForm::Triangle:
191 mLfoScale = 4.0f / mLfoRange;
192 break;
193 case WaveForm::Sinusoid:
194 mLfoScale = al::MathDefs<float>::Tau() / mLfoRange;
195 break;
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,
221 todo);
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,
228 todo);
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],
245 mu);
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],
252 mu);
254 // Accumulate feedback from the average delay of the taps.
255 delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
256 ++offset;
259 for(ALsizei c{0};c < 2;c++)
260 MixSamples({temps[c], todo}, samplesOut, mGains[c].Current, mGains[c].Target,
261 samplesToDo-base, base);
263 base += todo;
266 mOffset = offset;
270 void Chorus_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
272 switch(param)
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;
278 break;
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;
284 break;
286 default:
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)
294 switch(param)
296 case AL_CHORUS_RATE:
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;
300 break;
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;
306 break;
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;
312 break;
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;
318 break;
320 default:
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)
329 switch(param)
331 case AL_CHORUS_WAVEFORM:
332 *val = props->Chorus.Waveform;
333 break;
335 case AL_CHORUS_PHASE:
336 *val = props->Chorus.Phase;
337 break;
339 default:
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)
347 switch(param)
349 case AL_CHORUS_RATE:
350 *val = props->Chorus.Rate;
351 break;
353 case AL_CHORUS_DEPTH:
354 *val = props->Chorus.Depth;
355 break;
357 case AL_CHORUS_FEEDBACK:
358 *val = props->Chorus.Feedback;
359 break;
361 case AL_CHORUS_DELAY:
362 *val = props->Chorus.Delay;
363 break;
365 default:
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
383 EffectProps props{};
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;
390 return props;
394 void Flanger_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
396 switch(param)
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;
402 break;
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;
408 break;
410 default:
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)
418 switch(param)
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;
424 break;
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;
430 break;
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;
436 break;
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;
442 break;
444 default:
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)
453 switch(param)
455 case AL_FLANGER_WAVEFORM:
456 *val = props->Chorus.Waveform;
457 break;
459 case AL_FLANGER_PHASE:
460 *val = props->Chorus.Phase;
461 break;
463 default:
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)
471 switch(param)
473 case AL_FLANGER_RATE:
474 *val = props->Chorus.Rate;
475 break;
477 case AL_FLANGER_DEPTH:
478 *val = props->Chorus.Depth;
479 break;
481 case AL_FLANGER_FEEDBACK:
482 *val = props->Chorus.Feedback;
483 break;
485 case AL_FLANGER_DELAY:
486 *val = props->Chorus.Delay;
487 break;
489 default:
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
510 EffectProps props{};
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;
517 return props;
520 } // namespace
522 EffectStateFactory *ChorusStateFactory_getFactory()
524 static ChorusStateFactory ChorusFactory{};
525 return &ChorusFactory;
528 EffectStateFactory *FlangerStateFactory_getFactory()
530 static FlangerStateFactory FlangerFactory{};
531 return &FlangerFactory;