Use proper array sizes for more gains
[openal-soft.git] / alc / effects / fshifter.cpp
blobbed6c79f850dd3244d8bc55b50e9c8d790577dce
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2018 by Raul Herraiz.
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 <array>
25 #include <cmath>
26 #include <complex>
27 #include <cstdlib>
28 #include <iterator>
30 #include "alc/effects/base.h"
31 #include "alcomplex.h"
32 #include "almalloc.h"
33 #include "alnumbers.h"
34 #include "alnumeric.h"
35 #include "alspan.h"
36 #include "core/bufferline.h"
37 #include "core/context.h"
38 #include "core/devformat.h"
39 #include "core/device.h"
40 #include "core/effectslot.h"
41 #include "core/mixer.h"
42 #include "core/mixer/defs.h"
43 #include "intrusive_ptr.h"
46 namespace {
48 using uint = unsigned int;
49 using complex_d = std::complex<double>;
51 #define HIL_SIZE 1024
52 #define OVERSAMP (1<<2)
54 #define HIL_STEP (HIL_SIZE / OVERSAMP)
55 #define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
57 /* Define a Hann window, used to filter the HIL input and output. */
58 std::array<double,HIL_SIZE> InitHannWindow()
60 std::array<double,HIL_SIZE> ret;
61 /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
62 for(size_t i{0};i < HIL_SIZE>>1;i++)
64 constexpr double scale{al::numbers::pi / double{HIL_SIZE}};
65 const double val{std::sin(static_cast<double>(i+1) * scale)};
66 ret[i] = ret[HIL_SIZE-1-i] = val * val;
68 return ret;
70 alignas(16) const std::array<double,HIL_SIZE> HannWindow = InitHannWindow();
73 struct FshifterState final : public EffectState {
74 /* Effect parameters */
75 size_t mCount{};
76 size_t mPos{};
77 uint mPhaseStep[2]{};
78 uint mPhase[2]{};
79 double mSign[2]{};
81 /* Effects buffers */
82 double mInFIFO[HIL_SIZE]{};
83 complex_d mOutFIFO[HIL_STEP]{};
84 complex_d mOutputAccum[HIL_SIZE]{};
85 complex_d mAnalytic[HIL_SIZE]{};
86 complex_d mOutdata[BufferLineSize]{};
88 alignas(16) float mBufferOut[BufferLineSize]{};
90 /* Effect gains for each output channel */
91 struct {
92 float Current[MaxAmbiChannels]{};
93 float Target[MaxAmbiChannels]{};
94 } mGains[2];
97 void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
98 void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
99 const EffectTarget target) override;
100 void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
101 const al::span<FloatBufferLine> samplesOut) override;
103 DEF_NEWDEL(FshifterState)
106 void FshifterState::deviceUpdate(const DeviceBase*, const Buffer&)
108 /* (Re-)initializing parameters and clear the buffers. */
109 mCount = 0;
110 mPos = FIFO_LATENCY;
112 std::fill(std::begin(mPhaseStep), std::end(mPhaseStep), 0u);
113 std::fill(std::begin(mPhase), std::end(mPhase), 0u);
114 std::fill(std::begin(mSign), std::end(mSign), 1.0);
115 std::fill(std::begin(mInFIFO), std::end(mInFIFO), 0.0);
116 std::fill(std::begin(mOutFIFO), std::end(mOutFIFO), complex_d{});
117 std::fill(std::begin(mOutputAccum), std::end(mOutputAccum), complex_d{});
118 std::fill(std::begin(mAnalytic), std::end(mAnalytic), complex_d{});
120 for(auto &gain : mGains)
122 std::fill(std::begin(gain.Current), std::end(gain.Current), 0.0f);
123 std::fill(std::begin(gain.Target), std::end(gain.Target), 0.0f);
127 void FshifterState::update(const ContextBase *context, const EffectSlot *slot,
128 const EffectProps *props, const EffectTarget target)
130 const DeviceBase *device{context->mDevice};
132 const float step{props->Fshifter.Frequency / static_cast<float>(device->Frequency)};
133 mPhaseStep[0] = mPhaseStep[1] = fastf2u(minf(step, 1.0f) * MixerFracOne);
135 switch(props->Fshifter.LeftDirection)
137 case FShifterDirection::Down:
138 mSign[0] = -1.0;
139 break;
140 case FShifterDirection::Up:
141 mSign[0] = 1.0;
142 break;
143 case FShifterDirection::Off:
144 mPhase[0] = 0;
145 mPhaseStep[0] = 0;
146 break;
149 switch(props->Fshifter.RightDirection)
151 case FShifterDirection::Down:
152 mSign[1] = -1.0;
153 break;
154 case FShifterDirection::Up:
155 mSign[1] = 1.0;
156 break;
157 case FShifterDirection::Off:
158 mPhase[1] = 0;
159 mPhaseStep[1] = 0;
160 break;
163 const auto lcoeffs = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f}, 0.0f);
164 const auto rcoeffs = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}, 0.0f);
166 mOutTarget = target.Main->Buffer;
167 ComputePanGains(target.Main, lcoeffs.data(), slot->Gain, mGains[0].Target);
168 ComputePanGains(target.Main, rcoeffs.data(), slot->Gain, mGains[1].Target);
171 void FshifterState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
173 for(size_t base{0u};base < samplesToDo;)
175 size_t todo{minz(HIL_STEP-mCount, samplesToDo-base)};
177 /* Fill FIFO buffer with samples data */
178 const size_t pos{mPos};
179 size_t count{mCount};
180 do {
181 mInFIFO[pos+count] = samplesIn[0][base];
182 mOutdata[base] = mOutFIFO[count];
183 ++base; ++count;
184 } while(--todo);
185 mCount = count;
187 /* Check whether FIFO buffer is filled */
188 if(mCount < HIL_STEP) break;
189 mCount = 0;
190 mPos = (mPos+HIL_STEP) & (HIL_SIZE-1);
192 /* Real signal windowing and store in Analytic buffer */
193 for(size_t src{mPos}, k{0u};src < HIL_SIZE;++src,++k)
194 mAnalytic[k] = mInFIFO[src]*HannWindow[k];
195 for(size_t src{0u}, k{HIL_SIZE-mPos};src < mPos;++src,++k)
196 mAnalytic[k] = mInFIFO[src]*HannWindow[k];
198 /* Processing signal by Discrete Hilbert Transform (analytical signal). */
199 complex_hilbert(mAnalytic);
201 /* Windowing and add to output accumulator */
202 for(size_t dst{mPos}, k{0u};dst < HIL_SIZE;++dst,++k)
203 mOutputAccum[dst] += 2.0/OVERSAMP*HannWindow[k]*mAnalytic[k];
204 for(size_t dst{0u}, k{HIL_SIZE-mPos};dst < mPos;++dst,++k)
205 mOutputAccum[dst] += 2.0/OVERSAMP*HannWindow[k]*mAnalytic[k];
207 /* Copy out the accumulated result, then clear for the next iteration. */
208 std::copy_n(mOutputAccum + mPos, HIL_STEP, mOutFIFO);
209 std::fill_n(mOutputAccum + mPos, HIL_STEP, complex_d{});
212 /* Process frequency shifter using the analytic signal obtained. */
213 float *RESTRICT BufferOut{mBufferOut};
214 for(int c{0};c < 2;++c)
216 const uint phase_step{mPhaseStep[c]};
217 uint phase_idx{mPhase[c]};
218 for(size_t k{0};k < samplesToDo;++k)
220 const double phase{phase_idx * (al::numbers::pi*2.0 / MixerFracOne)};
221 BufferOut[k] = static_cast<float>(mOutdata[k].real()*std::cos(phase) +
222 mOutdata[k].imag()*std::sin(phase)*mSign[c]);
224 phase_idx += phase_step;
225 phase_idx &= MixerFracMask;
227 mPhase[c] = phase_idx;
229 /* Now, mix the processed sound data to the output. */
230 MixSamples({BufferOut, samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target,
231 maxz(samplesToDo, 512), 0);
236 struct FshifterStateFactory final : public EffectStateFactory {
237 al::intrusive_ptr<EffectState> create() override
238 { return al::intrusive_ptr<EffectState>{new FshifterState{}}; }
241 } // namespace
243 EffectStateFactory *FshifterStateFactory_getFactory()
245 static FshifterStateFactory FshifterFactory{};
246 return &FshifterFactory;