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
29 #include "alc/effects/base.h"
31 #include "alnumbers.h"
32 #include "alnumeric.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"
49 using uint
= unsigned int;
51 #define MAX_UPDATE_SAMPLES 256
53 struct ChorusState final
: public EffectState
{
54 al::vector
<float,16> mSampleBuffer
;
59 float mLfoScale
{0.0f
};
62 /* Gains for left and right sides */
64 float Current
[MAX_OUTPUT_CHANNELS
]{};
65 float Target
[MAX_OUTPUT_CHANNELS
]{};
68 /* effect parameters */
69 ChorusWaveform mWaveform
{};
72 float mFeedback
{0.0f
};
74 void getTriangleDelays(uint (*delays
)[MAX_UPDATE_SAMPLES
], const size_t todo
);
75 void getSinusoidDelays(uint (*delays
)[MAX_UPDATE_SAMPLES
], const size_t todo
);
77 void deviceUpdate(const DeviceBase
*device
, const Buffer
&buffer
) override
;
78 void update(const ContextBase
*context
, const EffectSlot
*slot
, const EffectProps
*props
,
79 const EffectTarget target
) override
;
80 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
,
81 const al::span
<FloatBufferLine
> samplesOut
) override
;
83 DEF_NEWDEL(ChorusState
)
86 void ChorusState::deviceUpdate(const DeviceBase
*Device
, const Buffer
&)
88 constexpr float max_delay
{maxf(ChorusMaxDelay
, FlangerMaxDelay
)};
90 const auto frequency
= static_cast<float>(Device
->Frequency
);
91 const size_t maxlen
{NextPowerOf2(float2uint(max_delay
*2.0f
*frequency
) + 1u)};
92 if(maxlen
!= mSampleBuffer
.size())
93 al::vector
<float,16>(maxlen
).swap(mSampleBuffer
);
95 std::fill(mSampleBuffer
.begin(), mSampleBuffer
.end(), 0.0f
);
98 std::fill(std::begin(e
.Current
), std::end(e
.Current
), 0.0f
);
99 std::fill(std::begin(e
.Target
), std::end(e
.Target
), 0.0f
);
103 void ChorusState::update(const ContextBase
*Context
, const EffectSlot
*Slot
,
104 const EffectProps
*props
, const EffectTarget target
)
106 constexpr int mindelay
{(MaxResamplerPadding
>>1) << MixerFracBits
};
108 /* The LFO depth is scaled to be relative to the sample delay. Clamp the
109 * delay and depth to allow enough padding for resampling.
111 const DeviceBase
*device
{Context
->mDevice
};
112 const auto frequency
= static_cast<float>(device
->Frequency
);
114 mWaveform
= props
->Chorus
.Waveform
;
116 mDelay
= maxi(float2int(props
->Chorus
.Delay
*frequency
*MixerFracOne
+ 0.5f
), mindelay
);
117 mDepth
= minf(props
->Chorus
.Depth
* static_cast<float>(mDelay
),
118 static_cast<float>(mDelay
- mindelay
));
120 mFeedback
= props
->Chorus
.Feedback
;
122 /* Gains for left and right sides */
123 const auto lcoeffs
= CalcDirectionCoeffs({-1.0f
, 0.0f
, 0.0f
}, 0.0f
);
124 const auto rcoeffs
= CalcDirectionCoeffs({ 1.0f
, 0.0f
, 0.0f
}, 0.0f
);
126 mOutTarget
= target
.Main
->Buffer
;
127 ComputePanGains(target
.Main
, lcoeffs
.data(), Slot
->Gain
, mGains
[0].Target
);
128 ComputePanGains(target
.Main
, rcoeffs
.data(), Slot
->Gain
, mGains
[1].Target
);
130 float rate
{props
->Chorus
.Rate
};
140 /* Calculate LFO coefficient (number of samples per cycle). Limit the
141 * max range to avoid overflow when calculating the displacement.
143 uint lfo_range
{float2uint(minf(frequency
/rate
+ 0.5f
, float{INT_MAX
/360 - 180}))};
145 mLfoOffset
= mLfoOffset
* lfo_range
/ mLfoRange
;
146 mLfoRange
= lfo_range
;
149 case ChorusWaveform::Triangle
:
150 mLfoScale
= 4.0f
/ static_cast<float>(mLfoRange
);
152 case ChorusWaveform::Sinusoid
:
153 mLfoScale
= al::numbers::pi_v
<float>*2.0f
/ static_cast<float>(mLfoRange
);
157 /* Calculate lfo phase displacement */
158 int phase
{props
->Chorus
.Phase
};
159 if(phase
< 0) phase
= 360 + phase
;
160 mLfoDisp
= (mLfoRange
*static_cast<uint
>(phase
) + 180) / 360;
165 void ChorusState::getTriangleDelays(uint (*delays
)[MAX_UPDATE_SAMPLES
], const size_t todo
)
167 const uint lfo_range
{mLfoRange
};
168 const float lfo_scale
{mLfoScale
};
169 const float depth
{mDepth
};
170 const int delay
{mDelay
};
172 ASSUME(lfo_range
> 0);
175 uint offset
{mLfoOffset
};
176 auto gen_lfo
= [&offset
,lfo_range
,lfo_scale
,depth
,delay
]() -> uint
178 offset
= (offset
+1)%lfo_range
;
179 const float offset_norm
{static_cast<float>(offset
) * lfo_scale
};
180 return static_cast<uint
>(fastf2i((1.0f
-std::abs(2.0f
-offset_norm
)) * depth
) + delay
);
182 std::generate_n(delays
[0], todo
, gen_lfo
);
184 offset
= (mLfoOffset
+mLfoDisp
) % lfo_range
;
185 std::generate_n(delays
[1], todo
, gen_lfo
);
187 mLfoOffset
= static_cast<uint
>(mLfoOffset
+todo
) % lfo_range
;
190 void ChorusState::getSinusoidDelays(uint (*delays
)[MAX_UPDATE_SAMPLES
], const size_t todo
)
192 const uint lfo_range
{mLfoRange
};
193 const float lfo_scale
{mLfoScale
};
194 const float depth
{mDepth
};
195 const int delay
{mDelay
};
197 ASSUME(lfo_range
> 0);
200 uint offset
{mLfoOffset
};
201 auto gen_lfo
= [&offset
,lfo_range
,lfo_scale
,depth
,delay
]() -> uint
203 offset
= (offset
+1)%lfo_range
;
204 const float offset_norm
{static_cast<float>(offset
) * lfo_scale
};
205 return static_cast<uint
>(fastf2i(std::sin(offset_norm
)*depth
) + delay
);
207 std::generate_n(delays
[0], todo
, gen_lfo
);
209 offset
= (mLfoOffset
+mLfoDisp
) % lfo_range
;
210 std::generate_n(delays
[1], todo
, gen_lfo
);
212 mLfoOffset
= static_cast<uint
>(mLfoOffset
+todo
) % lfo_range
;
215 void ChorusState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
217 const size_t bufmask
{mSampleBuffer
.size()-1};
218 const float feedback
{mFeedback
};
219 const uint avgdelay
{(static_cast<uint
>(mDelay
) + (MixerFracOne
>>1)) >> MixerFracBits
};
220 float *RESTRICT delaybuf
{mSampleBuffer
.data()};
221 uint offset
{mOffset
};
223 for(size_t base
{0u};base
< samplesToDo
;)
225 const size_t todo
{minz(MAX_UPDATE_SAMPLES
, samplesToDo
-base
)};
227 uint moddelays
[2][MAX_UPDATE_SAMPLES
];
228 if(mWaveform
== ChorusWaveform::Sinusoid
)
229 getSinusoidDelays(moddelays
, todo
);
230 else /*if(mWaveform == ChorusWaveform::Triangle)*/
231 getTriangleDelays(moddelays
, todo
);
233 alignas(16) float temps
[2][MAX_UPDATE_SAMPLES
];
234 for(size_t i
{0u};i
< todo
;++i
)
236 // Feed the buffer's input first (necessary for delays < 1).
237 delaybuf
[offset
&bufmask
] = samplesIn
[0][base
+i
];
239 // Tap for the left output.
240 uint delay
{offset
- (moddelays
[0][i
]>>MixerFracBits
)};
241 float mu
{static_cast<float>(moddelays
[0][i
]&MixerFracMask
) * (1.0f
/MixerFracOne
)};
242 temps
[0][i
] = cubic(delaybuf
[(delay
+1) & bufmask
], delaybuf
[(delay
) & bufmask
],
243 delaybuf
[(delay
-1) & bufmask
], delaybuf
[(delay
-2) & bufmask
], mu
);
245 // Tap for the right output.
246 delay
= offset
- (moddelays
[1][i
]>>MixerFracBits
);
247 mu
= static_cast<float>(moddelays
[1][i
]&MixerFracMask
) * (1.0f
/MixerFracOne
);
248 temps
[1][i
] = cubic(delaybuf
[(delay
+1) & bufmask
], delaybuf
[(delay
) & bufmask
],
249 delaybuf
[(delay
-1) & bufmask
], delaybuf
[(delay
-2) & bufmask
], mu
);
251 // Accumulate feedback from the average delay of the taps.
252 delaybuf
[offset
&bufmask
] += delaybuf
[(offset
-avgdelay
) & bufmask
] * feedback
;
256 for(size_t c
{0};c
< 2;++c
)
257 MixSamples({temps
[c
], todo
}, samplesOut
, mGains
[c
].Current
, mGains
[c
].Target
,
258 samplesToDo
-base
, base
);
267 struct ChorusStateFactory final
: public EffectStateFactory
{
268 al::intrusive_ptr
<EffectState
> create() override
269 { return al::intrusive_ptr
<EffectState
>{new ChorusState
{}}; }
273 /* Flanger is basically a chorus with a really short delay. They can both use
274 * the same processing functions, so piggyback flanger on the chorus functions.
276 struct FlangerStateFactory final
: public EffectStateFactory
{
277 al::intrusive_ptr
<EffectState
> create() override
278 { return al::intrusive_ptr
<EffectState
>{new ChorusState
{}}; }
283 EffectStateFactory
*ChorusStateFactory_getFactory()
285 static ChorusStateFactory ChorusFactory
{};
286 return &ChorusFactory
;
289 EffectStateFactory
*FlangerStateFactory_getFactory()
291 static FlangerStateFactory FlangerFactory
{};
292 return &FlangerFactory
;