2 * OpenAL cross platform audio library
3 * Copyright (C) 2009 by Chris Robinson.
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/device.h"
39 #include "core/effects/base.h"
40 #include "core/effectslot.h"
41 #include "core/filters/biquad.h"
42 #include "core/mixer.h"
43 #include "intrusive_ptr.h"
44 #include "opthelpers.h"
50 using uint
= unsigned int;
53 static auto Get(uint index
, float scale
) noexcept(noexcept(std::sin(0.0f
))) -> float
54 { return std::sin(static_cast<float>(index
) * scale
); }
58 static constexpr auto Get(uint index
, float scale
) noexcept
-> float
59 { return static_cast<float>(index
)*scale
- 1.0f
; }
63 static constexpr auto Get(uint index
, float scale
) noexcept
-> float
64 { return float(static_cast<float>(index
)*scale
< 0.5f
)*2.0f
- 1.0f
; }
68 static constexpr auto Get(uint
, float) noexcept
-> float
73 struct ModulatorState final
: public EffectState
{
74 std::variant
<OneFunc
,SinFunc
,SawFunc
,SquareFunc
> mSampleGen
;
78 float mIndexScale
{0.0f
};
80 alignas(16) FloatBufferLine mModSamples
{};
81 alignas(16) FloatBufferLine mBuffer
{};
84 uint mTargetChannel
{InvalidChannelIndex
};
91 std::array
<OutParams
,MaxAmbiChannels
> mChans
;
94 void deviceUpdate(const DeviceBase
*device
, const BufferStorage
*buffer
) override
;
95 void update(const ContextBase
*context
, const EffectSlot
*slot
, const EffectProps
*props
,
96 const EffectTarget target
) override
;
97 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
,
98 const al::span
<FloatBufferLine
> samplesOut
) override
;
101 void ModulatorState::deviceUpdate(const DeviceBase
*, const BufferStorage
*)
103 for(auto &e
: mChans
)
105 e
.mTargetChannel
= InvalidChannelIndex
;
107 e
.mCurrentGain
= 0.0f
;
111 void ModulatorState::update(const ContextBase
*context
, const EffectSlot
*slot
,
112 const EffectProps
*props_
, const EffectTarget target
)
114 auto &props
= std::get
<ModulatorProps
>(*props_
);
115 const DeviceBase
*device
{context
->mDevice
};
117 /* The effective frequency will be adjusted to have a whole number of
118 * samples per cycle (at 48khz, that allows 8000, 6857.14, 6000, 5333.33,
119 * 4800, etc). We could do better by using fixed-point stepping over a sin
120 * function, with additive synthesis for the square and sawtooth waveforms,
121 * but that may need a more efficient sin function since it needs to do
122 * many iterations per sample.
124 const float samplesPerCycle
{props
.Frequency
> 0.0f
125 ? static_cast<float>(device
->Frequency
)/props
.Frequency
+ 0.5f
127 const uint range
{static_cast<uint
>(std::clamp(samplesPerCycle
, 1.0f
,
128 static_cast<float>(device
->Frequency
)))};
129 mIndex
= static_cast<uint
>(uint64_t{mIndex
} * range
/ mRange
);
135 mSampleGen
.emplace
<OneFunc
>();
137 else if(props
.Waveform
== ModulatorWaveform::Sinusoid
)
139 mIndexScale
= al::numbers::pi_v
<float>*2.0f
/ static_cast<float>(mRange
);
140 mSampleGen
.emplace
<SinFunc
>();
142 else if(props
.Waveform
== ModulatorWaveform::Sawtooth
)
144 mIndexScale
= 2.0f
/ static_cast<float>(mRange
-1);
145 mSampleGen
.emplace
<SawFunc
>();
147 else if(props
.Waveform
== ModulatorWaveform::Square
)
149 /* For square wave, the range should be even (there should be an equal
150 * number of high and low samples). An odd number of samples per cycle
151 * would need a more complex value generator.
153 mRange
= (mRange
+1) & ~1u;
154 mIndexScale
= 1.0f
/ static_cast<float>(mRange
-1);
155 mSampleGen
.emplace
<SquareFunc
>();
158 float f0norm
{props
.HighPassCutoff
/ static_cast<float>(device
->Frequency
)};
159 f0norm
= std::clamp(f0norm
, 1.0f
/512.0f
, 0.49f
);
160 /* Bandwidth value is constant in octaves. */
161 mChans
[0].mFilter
.setParamsFromBandwidth(BiquadType::HighPass
, f0norm
, 1.0f
, 0.75f
);
162 for(size_t i
{1u};i
< slot
->Wet
.Buffer
.size();++i
)
163 mChans
[i
].mFilter
.copyParamsFrom(mChans
[0].mFilter
);
165 mOutTarget
= target
.Main
->Buffer
;
166 auto set_channel
= [this](size_t idx
, uint outchan
, float outgain
)
168 mChans
[idx
].mTargetChannel
= outchan
;
169 mChans
[idx
].mTargetGain
= outgain
;
171 target
.Main
->setAmbiMixParams(slot
->Wet
, slot
->Gain
, set_channel
);
174 void ModulatorState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
176 ASSUME(samplesToDo
> 0);
178 std::visit([this,samplesToDo
](auto&& type
)
180 const uint range
{mRange
};
181 const float scale
{mIndexScale
};
186 for(size_t i
{0};i
< samplesToDo
;)
188 size_t rem
{std::min(samplesToDo
-i
, size_t{range
-index
})};
190 mModSamples
[i
++] = type
.Get(index
++, scale
);
198 auto chandata
= mChans
.begin();
199 for(const auto &input
: samplesIn
)
201 if(const size_t outidx
{chandata
->mTargetChannel
}; outidx
!= InvalidChannelIndex
)
203 chandata
->mFilter
.process(al::span
{input
}.first(samplesToDo
), mBuffer
);
204 std::transform(mBuffer
.cbegin(), mBuffer
.cbegin()+samplesToDo
, mModSamples
.cbegin(),
205 mBuffer
.begin(), std::multiplies
<>{});
207 MixSamples(al::span
{mBuffer
}.first(samplesToDo
), samplesOut
[outidx
],
208 chandata
->mCurrentGain
, chandata
->mTargetGain
, std::min(samplesToDo
, 64_uz
));
215 struct ModulatorStateFactory final
: public EffectStateFactory
{
216 al::intrusive_ptr
<EffectState
> create() override
217 { return al::intrusive_ptr
<EffectState
>{new ModulatorState
{}}; }
222 EffectStateFactory
*ModulatorStateFactory_getFactory()
224 static ModulatorStateFactory ModulatorFactory
{};
225 return &ModulatorFactory
;