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
31 #include "alc/effects/base.h"
32 #include "alnumbers.h"
33 #include "alnumeric.h"
35 #include "core/ambidefs.h"
36 #include "core/bufferline.h"
37 #include "core/context.h"
38 #include "core/cubic_tables.h"
39 #include "core/device.h"
40 #include "core/effects/base.h"
41 #include "core/effectslot.h"
42 #include "core/mixer.h"
43 #include "core/mixer/defs.h"
44 #include "core/resampler_limits.h"
45 #include "intrusive_ptr.h"
46 #include "opthelpers.h"
52 using uint
= unsigned int;
54 constexpr auto inv_sqrt2
= static_cast<float>(1.0 / al::numbers::sqrt2
);
55 constexpr auto lcoeffs_pw
= CalcDirectionCoeffs(std::array
{-1.0f
, 0.0f
, 0.0f
});
56 constexpr auto rcoeffs_pw
= CalcDirectionCoeffs(std::array
{ 1.0f
, 0.0f
, 0.0f
});
57 constexpr auto lcoeffs_nrml
= CalcDirectionCoeffs(std::array
{-inv_sqrt2
, 0.0f
, inv_sqrt2
});
58 constexpr auto rcoeffs_nrml
= CalcDirectionCoeffs(std::array
{ inv_sqrt2
, 0.0f
, inv_sqrt2
});
61 struct ChorusState final
: public EffectState
{
62 std::vector
<float> mDelayBuffer
;
67 float mLfoScale
{0.0f
};
70 /* Calculated delays to apply to the left and right outputs. */
71 std::array
<std::array
<uint
,BufferLineSize
>,2> mModDelays
{};
73 /* Temp storage for the modulated left and right outputs. */
74 alignas(16) std::array
<FloatBufferLine
,2> mBuffer
{};
76 /* Gains for left and right outputs. */
78 std::array
<float,MaxAmbiChannels
> Current
{};
79 std::array
<float,MaxAmbiChannels
> Target
{};
81 std::array
<OutGains
,2> mGains
;
83 /* effect parameters */
84 ChorusWaveform mWaveform
{};
87 float mFeedback
{0.0f
};
89 void calcTriangleDelays(const size_t todo
);
90 void calcSinusoidDelays(const size_t todo
);
92 void deviceUpdate(const DeviceBase
*device
, const float MaxDelay
);
93 void update(const ContextBase
*context
, const EffectSlot
*slot
, const ChorusWaveform waveform
,
94 const float delay
, const float depth
, const float feedback
, const float rate
,
95 int phase
, const EffectTarget target
);
97 void deviceUpdate(const DeviceBase
*device
, const BufferStorage
*) final
;
98 void update(const ContextBase
*context
, const EffectSlot
*slot
, const EffectProps
*props_
,
99 const EffectTarget target
) final
;
100 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
,
101 const al::span
<FloatBufferLine
> samplesOut
) final
;
105 void ChorusState::deviceUpdate(const DeviceBase
*Device
, const BufferStorage
*)
107 constexpr auto MaxDelay
= std::max(ChorusMaxDelay
, FlangerMaxDelay
);
108 const auto frequency
= static_cast<float>(Device
->Frequency
);
109 const size_t maxlen
{NextPowerOf2(float2uint(MaxDelay
*2.0f
*frequency
) + 1u)};
110 if(maxlen
!= mDelayBuffer
.size())
111 decltype(mDelayBuffer
)(maxlen
).swap(mDelayBuffer
);
113 std::fill(mDelayBuffer
.begin(), mDelayBuffer
.end(), 0.0f
);
114 for(auto &e
: mGains
)
116 e
.Current
.fill(0.0f
);
121 void ChorusState::update(const ContextBase
*context
, const EffectSlot
*slot
,
122 const EffectProps
*props_
, const EffectTarget target
)
124 static constexpr int mindelay
{MaxResamplerEdge
<< gCubicTable
.sTableBits
};
125 auto &props
= std::get
<ChorusProps
>(*props_
);
127 /* The LFO depth is scaled to be relative to the sample delay. Clamp the
128 * delay and depth to allow enough padding for resampling.
130 const DeviceBase
*device
{context
->mDevice
};
131 const auto frequency
= static_cast<float>(device
->Frequency
);
133 mWaveform
= props
.Waveform
;
135 const auto stepscale
= float{frequency
* gCubicTable
.sTableSteps
};
136 mDelay
= std::max(float2int(std::round(props
.Delay
* stepscale
)), mindelay
);
137 mDepth
= std::min(static_cast<float>(mDelay
) * props
.Depth
,
138 static_cast<float>(mDelay
- mindelay
));
140 mFeedback
= props
.Feedback
;
142 /* Gains for left and right sides */
143 const bool ispairwise
{device
->mRenderMode
== RenderMode::Pairwise
};
144 const auto lcoeffs
= (!ispairwise
) ? al::span
{lcoeffs_nrml
} : al::span
{lcoeffs_pw
};
145 const auto rcoeffs
= (!ispairwise
) ? al::span
{rcoeffs_nrml
} : al::span
{rcoeffs_pw
};
147 /* Attenuate the outputs by -3dB, since we duplicate a single mono input to
148 * separate left/right outputs.
150 const auto gain
= slot
->Gain
* (1.0f
/al::numbers::sqrt2_v
<float>);
151 mOutTarget
= target
.Main
->Buffer
;
152 ComputePanGains(target
.Main
, lcoeffs
, gain
, mGains
[0].Target
);
153 ComputePanGains(target
.Main
, rcoeffs
, gain
, mGains
[1].Target
);
155 if(!(props
.Rate
> 0.0f
))
164 /* Calculate LFO coefficient (number of samples per cycle). Limit the
165 * max range to avoid overflow when calculating the displacement.
167 static constexpr int range_limit
{std::numeric_limits
<int>::max()/360 - 180};
168 const auto range
= std::round(frequency
/ props
.Rate
);
169 const uint lfo_range
{float2uint(std::min(range
, float{range_limit
}))};
171 mLfoOffset
= mLfoOffset
* lfo_range
/ mLfoRange
;
172 mLfoRange
= lfo_range
;
175 case ChorusWaveform::Triangle
:
176 mLfoScale
= 4.0f
/ static_cast<float>(mLfoRange
);
178 case ChorusWaveform::Sinusoid
:
179 mLfoScale
= al::numbers::pi_v
<float>*2.0f
/ static_cast<float>(mLfoRange
);
183 /* Calculate lfo phase displacement */
184 auto phase
= props
.Phase
;
185 if(phase
< 0) phase
+= 360;
186 mLfoDisp
= (mLfoRange
*static_cast<uint
>(phase
) + 180) / 360;
191 void ChorusState::calcTriangleDelays(const size_t todo
)
193 const uint lfo_range
{mLfoRange
};
194 const float lfo_scale
{mLfoScale
};
195 const float depth
{mDepth
};
196 const int delay
{mDelay
};
198 auto gen_lfo
= [lfo_scale
,depth
,delay
](const uint offset
) -> uint
200 const float offset_norm
{static_cast<float>(offset
) * lfo_scale
};
201 return static_cast<uint
>(fastf2i((1.0f
-std::abs(2.0f
-offset_norm
)) * depth
) + delay
);
204 uint offset
{mLfoOffset
};
205 ASSUME(lfo_range
> offset
);
206 auto ldelays
= mModDelays
[0].begin();
207 for(size_t i
{0};i
< todo
;)
209 const size_t rem
{std::min(todo
-i
, size_t{lfo_range
-offset
})};
210 ldelays
= std::generate_n(ldelays
, rem
, [&offset
,gen_lfo
] { return gen_lfo(offset
++); });
211 if(offset
== lfo_range
) offset
= 0;
215 offset
= (mLfoOffset
+mLfoDisp
) % lfo_range
;
216 auto rdelays
= mModDelays
[1].begin();
217 for(size_t i
{0};i
< todo
;)
219 const size_t rem
{std::min(todo
-i
, size_t{lfo_range
-offset
})};
220 rdelays
= std::generate_n(rdelays
, rem
, [&offset
,gen_lfo
] { return gen_lfo(offset
++); });
221 if(offset
== lfo_range
) offset
= 0;
225 mLfoOffset
= static_cast<uint
>(mLfoOffset
+todo
) % lfo_range
;
228 void ChorusState::calcSinusoidDelays(const size_t todo
)
230 const uint lfo_range
{mLfoRange
};
231 const float lfo_scale
{mLfoScale
};
232 const float depth
{mDepth
};
233 const int delay
{mDelay
};
235 auto gen_lfo
= [lfo_scale
,depth
,delay
](const uint offset
) -> uint
237 const float offset_norm
{static_cast<float>(offset
) * lfo_scale
};
238 return static_cast<uint
>(fastf2i(std::sin(offset_norm
)*depth
) + delay
);
241 uint offset
{mLfoOffset
};
242 ASSUME(lfo_range
> offset
);
243 auto ldelays
= mModDelays
[0].begin();
244 for(size_t i
{0};i
< todo
;)
246 const size_t rem
{std::min(todo
-i
, size_t{lfo_range
-offset
})};
247 ldelays
= std::generate_n(ldelays
, rem
, [&offset
,gen_lfo
] { return gen_lfo(offset
++); });
248 if(offset
== lfo_range
) offset
= 0;
252 offset
= (mLfoOffset
+mLfoDisp
) % lfo_range
;
253 auto rdelays
= mModDelays
[1].begin();
254 for(size_t i
{0};i
< todo
;)
256 const size_t rem
{std::min(todo
-i
, size_t{lfo_range
-offset
})};
257 rdelays
= std::generate_n(rdelays
, rem
, [&offset
,gen_lfo
] { return gen_lfo(offset
++); });
258 if(offset
== lfo_range
) offset
= 0;
262 mLfoOffset
= static_cast<uint
>(mLfoOffset
+todo
) % lfo_range
;
265 void ChorusState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
267 const auto delaybuf
= al::span
{mDelayBuffer
};
268 const size_t bufmask
{delaybuf
.size()-1};
269 const float feedback
{mFeedback
};
270 const uint avgdelay
{(static_cast<uint
>(mDelay
) + MixerFracHalf
) >> MixerFracBits
};
271 uint offset
{mOffset
};
273 if(mWaveform
== ChorusWaveform::Sinusoid
)
274 calcSinusoidDelays(samplesToDo
);
275 else /*if(mWaveform == ChorusWaveform::Triangle)*/
276 calcTriangleDelays(samplesToDo
);
278 const auto ldelays
= al::span
{mModDelays
[0]};
279 const auto rdelays
= al::span
{mModDelays
[1]};
280 const auto lbuffer
= al::span
{mBuffer
[0]};
281 const auto rbuffer
= al::span
{mBuffer
[1]};
282 for(size_t i
{0u};i
< samplesToDo
;++i
)
284 // Feed the buffer's input first (necessary for delays < 1).
285 delaybuf
[offset
&bufmask
] = samplesIn
[0][i
];
287 // Tap for the left output.
288 size_t delay
{offset
- (ldelays
[i
] >> gCubicTable
.sTableBits
)};
289 size_t phase
{ldelays
[i
] & gCubicTable
.sTableMask
};
290 lbuffer
[i
] = delaybuf
[(delay
+1) & bufmask
]*gCubicTable
.getCoeff0(phase
) +
291 delaybuf
[(delay
) & bufmask
]*gCubicTable
.getCoeff1(phase
) +
292 delaybuf
[(delay
-1) & bufmask
]*gCubicTable
.getCoeff2(phase
) +
293 delaybuf
[(delay
-2) & bufmask
]*gCubicTable
.getCoeff3(phase
);
295 // Tap for the right output.
296 delay
= offset
- (rdelays
[i
] >> gCubicTable
.sTableBits
);
297 phase
= rdelays
[i
] & gCubicTable
.sTableMask
;
298 rbuffer
[i
] = delaybuf
[(delay
+1) & bufmask
]*gCubicTable
.getCoeff0(phase
) +
299 delaybuf
[(delay
) & bufmask
]*gCubicTable
.getCoeff1(phase
) +
300 delaybuf
[(delay
-1) & bufmask
]*gCubicTable
.getCoeff2(phase
) +
301 delaybuf
[(delay
-2) & bufmask
]*gCubicTable
.getCoeff3(phase
);
303 // Accumulate feedback from the average delay of the taps.
304 delaybuf
[offset
&bufmask
] += delaybuf
[(offset
-avgdelay
) & bufmask
] * feedback
;
308 MixSamples(lbuffer
.first(samplesToDo
), samplesOut
, mGains
[0].Current
, mGains
[0].Target
,
310 MixSamples(rbuffer
.first(samplesToDo
), samplesOut
, mGains
[1].Current
, mGains
[1].Target
,
317 struct ChorusStateFactory final
: public EffectStateFactory
{
318 al::intrusive_ptr
<EffectState
> create() override
319 { return al::intrusive_ptr
<EffectState
>{new ChorusState
{}}; }
324 EffectStateFactory
*ChorusStateFactory_getFactory()
326 static ChorusStateFactory ChorusFactory
{};
327 return &ChorusFactory
;