]> git.tdb.fi Git - ext/openal.git/blob - alc/effects/chorus.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / alc / effects / chorus.cpp
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.
8  *
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.
13  *
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
19  */
20
21 #include "config.h"
22
23 #include <algorithm>
24 #include <array>
25 #include <climits>
26 #include <cstdlib>
27 #include <iterator>
28
29 #include "alc/effects/base.h"
30 #include "almalloc.h"
31 #include "alnumbers.h"
32 #include "alnumeric.h"
33 #include "alspan.h"
34 #include "core/bufferline.h"
35 #include "core/context.h"
36 #include "core/devformat.h"
37 #include "core/device.h"
38 #include "core/effectslot.h"
39 #include "core/mixer.h"
40 #include "core/mixer/defs.h"
41 #include "core/resampler_limits.h"
42 #include "intrusive_ptr.h"
43 #include "opthelpers.h"
44 #include "vector.h"
45
46
47 namespace {
48
49 using uint = unsigned int;
50
51 struct ChorusState final : public EffectState {
52     al::vector<float,16> mDelayBuffer;
53     uint mOffset{0};
54
55     uint mLfoOffset{0};
56     uint mLfoRange{1};
57     float mLfoScale{0.0f};
58     uint mLfoDisp{0};
59
60     /* Calculated delays to apply to the left and right outputs. */
61     uint mModDelays[2][BufferLineSize];
62
63     /* Temp storage for the modulated left and right outputs. */
64     alignas(16) float mBuffer[2][BufferLineSize];
65
66     /* Gains for left and right outputs. */
67     struct {
68         float Current[MaxAmbiChannels]{};
69         float Target[MaxAmbiChannels]{};
70     } mGains[2];
71
72     /* effect parameters */
73     ChorusWaveform mWaveform{};
74     int mDelay{0};
75     float mDepth{0.0f};
76     float mFeedback{0.0f};
77
78     void calcTriangleDelays(const size_t todo);
79     void calcSinusoidDelays(const size_t todo);
80
81     void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override;
82     void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
83         const EffectTarget target) override;
84     void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
85         const al::span<FloatBufferLine> samplesOut) override;
86
87     DEF_NEWDEL(ChorusState)
88 };
89
90 void ChorusState::deviceUpdate(const DeviceBase *Device, const BufferStorage*)
91 {
92     constexpr float max_delay{maxf(ChorusMaxDelay, FlangerMaxDelay)};
93
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 != mDelayBuffer.size())
97         decltype(mDelayBuffer)(maxlen).swap(mDelayBuffer);
98
99     std::fill(mDelayBuffer.begin(), mDelayBuffer.end(), 0.0f);
100     for(auto &e : mGains)
101     {
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);
104     }
105 }
106
107 void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot,
108     const EffectProps *props, const EffectTarget target)
109 {
110     constexpr int mindelay{(MaxResamplerPadding>>1) << MixerFracBits};
111
112     /* The LFO depth is scaled to be relative to the sample delay. Clamp the
113      * delay and depth to allow enough padding for resampling.
114      */
115     const DeviceBase *device{Context->mDevice};
116     const auto frequency = static_cast<float>(device->Frequency);
117
118     mWaveform = props->Chorus.Waveform;
119
120     mDelay = maxi(float2int(props->Chorus.Delay*frequency*MixerFracOne + 0.5f), mindelay);
121     mDepth = minf(props->Chorus.Depth * static_cast<float>(mDelay),
122         static_cast<float>(mDelay - mindelay));
123
124     mFeedback = props->Chorus.Feedback;
125
126     /* Gains for left and right sides */
127     static constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2);
128     static constexpr auto lcoeffs_pw = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f});
129     static constexpr auto rcoeffs_pw = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f});
130     static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs({-inv_sqrt2, 0.0f, inv_sqrt2});
131     static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs({ inv_sqrt2, 0.0f, inv_sqrt2});
132     auto &lcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? lcoeffs_nrml : lcoeffs_pw;
133     auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw;
134
135     mOutTarget = target.Main->Buffer;
136     ComputePanGains(target.Main, lcoeffs.data(), Slot->Gain, mGains[0].Target);
137     ComputePanGains(target.Main, rcoeffs.data(), Slot->Gain, mGains[1].Target);
138
139     float rate{props->Chorus.Rate};
140     if(!(rate > 0.0f))
141     {
142         mLfoOffset = 0;
143         mLfoRange = 1;
144         mLfoScale = 0.0f;
145         mLfoDisp = 0;
146     }
147     else
148     {
149         /* Calculate LFO coefficient (number of samples per cycle). Limit the
150          * max range to avoid overflow when calculating the displacement.
151          */
152         uint lfo_range{float2uint(minf(frequency/rate + 0.5f, float{INT_MAX/360 - 180}))};
153
154         mLfoOffset = mLfoOffset * lfo_range / mLfoRange;
155         mLfoRange = lfo_range;
156         switch(mWaveform)
157         {
158         case ChorusWaveform::Triangle:
159             mLfoScale = 4.0f / static_cast<float>(mLfoRange);
160             break;
161         case ChorusWaveform::Sinusoid:
162             mLfoScale = al::numbers::pi_v<float>*2.0f / static_cast<float>(mLfoRange);
163             break;
164         }
165
166         /* Calculate lfo phase displacement */
167         int phase{props->Chorus.Phase};
168         if(phase < 0) phase = 360 + phase;
169         mLfoDisp = (mLfoRange*static_cast<uint>(phase) + 180) / 360;
170     }
171 }
172
173
174 void ChorusState::calcTriangleDelays(const size_t todo)
175 {
176     const uint lfo_range{mLfoRange};
177     const float lfo_scale{mLfoScale};
178     const float depth{mDepth};
179     const int delay{mDelay};
180
181     ASSUME(lfo_range > 0);
182     ASSUME(todo > 0);
183
184     auto gen_lfo = [lfo_scale,depth,delay](const uint offset) -> uint
185     {
186         const float offset_norm{static_cast<float>(offset) * lfo_scale};
187         return static_cast<uint>(fastf2i((1.0f-std::abs(2.0f-offset_norm)) * depth) + delay);
188     };
189
190     uint offset{mLfoOffset};
191     for(size_t i{0};i < todo;)
192     {
193         size_t rem{minz(todo-i, lfo_range-offset)};
194         do {
195             mModDelays[0][i++] = gen_lfo(offset++);
196         } while(--rem);
197         if(offset == lfo_range)
198             offset = 0;
199     }
200
201     offset = (mLfoOffset+mLfoDisp) % lfo_range;
202     for(size_t i{0};i < todo;)
203     {
204         size_t rem{minz(todo-i, lfo_range-offset)};
205         do {
206             mModDelays[1][i++] = gen_lfo(offset++);
207         } while(--rem);
208         if(offset == lfo_range)
209             offset = 0;
210     }
211
212     mLfoOffset = static_cast<uint>(mLfoOffset+todo) % lfo_range;
213 }
214
215 void ChorusState::calcSinusoidDelays(const size_t todo)
216 {
217     const uint lfo_range{mLfoRange};
218     const float lfo_scale{mLfoScale};
219     const float depth{mDepth};
220     const int delay{mDelay};
221
222     ASSUME(lfo_range > 0);
223     ASSUME(todo > 0);
224
225     auto gen_lfo = [lfo_scale,depth,delay](const uint offset) -> uint
226     {
227         const float offset_norm{static_cast<float>(offset) * lfo_scale};
228         return static_cast<uint>(fastf2i(std::sin(offset_norm)*depth) + delay);
229     };
230
231     uint offset{mLfoOffset};
232     for(size_t i{0};i < todo;)
233     {
234         size_t rem{minz(todo-i, lfo_range-offset)};
235         do {
236             mModDelays[0][i++] = gen_lfo(offset++);
237         } while(--rem);
238         if(offset == lfo_range)
239             offset = 0;
240     }
241
242     offset = (mLfoOffset+mLfoDisp) % lfo_range;
243     for(size_t i{0};i < todo;)
244     {
245         size_t rem{minz(todo-i, lfo_range-offset)};
246         do {
247             mModDelays[1][i++] = gen_lfo(offset++);
248         } while(--rem);
249         if(offset == lfo_range)
250             offset = 0;
251     }
252
253     mLfoOffset = static_cast<uint>(mLfoOffset+todo) % lfo_range;
254 }
255
256 void ChorusState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
257 {
258     const size_t bufmask{mDelayBuffer.size()-1};
259     const float feedback{mFeedback};
260     const uint avgdelay{(static_cast<uint>(mDelay) + MixerFracHalf) >> MixerFracBits};
261     float *RESTRICT delaybuf{mDelayBuffer.data()};
262     uint offset{mOffset};
263
264     if(mWaveform == ChorusWaveform::Sinusoid)
265         calcSinusoidDelays(samplesToDo);
266     else /*if(mWaveform == ChorusWaveform::Triangle)*/
267         calcTriangleDelays(samplesToDo);
268
269     const uint *RESTRICT ldelays{mModDelays[0]};
270     const uint *RESTRICT rdelays{mModDelays[1]};
271     float *RESTRICT lbuffer{al::assume_aligned<16>(mBuffer[0])};
272     float *RESTRICT rbuffer{al::assume_aligned<16>(mBuffer[1])};
273     for(size_t i{0u};i < samplesToDo;++i)
274     {
275         // Feed the buffer's input first (necessary for delays < 1).
276         delaybuf[offset&bufmask] = samplesIn[0][i];
277
278         // Tap for the left output.
279         uint delay{offset - (ldelays[i]>>MixerFracBits)};
280         float mu{static_cast<float>(ldelays[i]&MixerFracMask) * (1.0f/MixerFracOne)};
281         lbuffer[i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay  ) & bufmask],
282             delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu);
283
284         // Tap for the right output.
285         delay = offset - (rdelays[i]>>MixerFracBits);
286         mu = static_cast<float>(rdelays[i]&MixerFracMask) * (1.0f/MixerFracOne);
287         rbuffer[i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay  ) & bufmask],
288             delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu);
289
290         // Accumulate feedback from the average delay of the taps.
291         delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
292         ++offset;
293     }
294
295     MixSamples({lbuffer, samplesToDo}, samplesOut, mGains[0].Current, mGains[0].Target,
296         samplesToDo, 0);
297     MixSamples({rbuffer, samplesToDo}, samplesOut, mGains[1].Current, mGains[1].Target,
298         samplesToDo, 0);
299
300     mOffset = offset;
301 }
302
303
304 struct ChorusStateFactory final : public EffectStateFactory {
305     al::intrusive_ptr<EffectState> create() override
306     { return al::intrusive_ptr<EffectState>{new ChorusState{}}; }
307 };
308
309
310 /* Flanger is basically a chorus with a really short delay. They can both use
311  * the same processing functions, so piggyback flanger on the chorus functions.
312  */
313 struct FlangerStateFactory final : public EffectStateFactory {
314     al::intrusive_ptr<EffectState> create() override
315     { return al::intrusive_ptr<EffectState>{new ChorusState{}}; }
316 };
317
318 } // namespace
319
320 EffectStateFactory *ChorusStateFactory_getFactory()
321 {
322     static ChorusStateFactory ChorusFactory{};
323     return &ChorusFactory;
324 }
325
326 EffectStateFactory *FlangerStateFactory_getFactory()
327 {
328     static FlangerStateFactory FlangerFactory{};
329     return &FlangerFactory;
330 }