]> git.tdb.fi Git - ext/openal.git/blob - alc/effects/fshifter.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / alc / effects / fshifter.cpp
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.
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 <cmath>
26 #include <complex>
27 #include <cstdlib>
28 #include <iterator>
29
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"
44
45
46 namespace {
47
48 using uint = unsigned int;
49 using complex_d = std::complex<double>;
50
51 constexpr size_t HilSize{1024};
52 constexpr size_t HilHalfSize{HilSize >> 1};
53 constexpr size_t OversampleFactor{4};
54
55 static_assert(HilSize%OversampleFactor == 0, "Factor must be a clean divisor of the size");
56 constexpr size_t HilStep{HilSize / OversampleFactor};
57
58 /* Define a Hann window, used to filter the HIL input and output. */
59 struct Windower {
60     alignas(16) std::array<double,HilSize> mData;
61
62     Windower()
63     {
64         /* Create lookup table of the Hann window for the desired size. */
65         for(size_t i{0};i < HilHalfSize;i++)
66         {
67             constexpr double scale{al::numbers::pi / double{HilSize}};
68             const double val{std::sin((static_cast<double>(i)+0.5) * scale)};
69             mData[i] = mData[HilSize-1-i] = val * val;
70         }
71     }
72 };
73 const Windower gWindow{};
74
75
76 struct FshifterState final : public EffectState {
77     /* Effect parameters */
78     size_t mCount{};
79     size_t mPos{};
80     std::array<uint,2> mPhaseStep{};
81     std::array<uint,2> mPhase{};
82     std::array<double,2> mSign{};
83
84     /* Effects buffers */
85     std::array<double,HilSize> mInFIFO{};
86     std::array<complex_d,HilStep> mOutFIFO{};
87     std::array<complex_d,HilSize> mOutputAccum{};
88     std::array<complex_d,HilSize> mAnalytic{};
89     std::array<complex_d,BufferLineSize> mOutdata{};
90
91     alignas(16) FloatBufferLine mBufferOut{};
92
93     /* Effect gains for each output channel */
94     struct {
95         float Current[MaxAmbiChannels]{};
96         float Target[MaxAmbiChannels]{};
97     } mGains[2];
98
99
100     void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override;
101     void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
102         const EffectTarget target) override;
103     void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
104         const al::span<FloatBufferLine> samplesOut) override;
105
106     DEF_NEWDEL(FshifterState)
107 };
108
109 void FshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*)
110 {
111     /* (Re-)initializing parameters and clear the buffers. */
112     mCount = 0;
113     mPos = HilSize - HilStep;
114
115     mPhaseStep.fill(0u);
116     mPhase.fill(0u);
117     mSign.fill(1.0);
118     mInFIFO.fill(0.0);
119     mOutFIFO.fill(complex_d{});
120     mOutputAccum.fill(complex_d{});
121     mAnalytic.fill(complex_d{});
122
123     for(auto &gain : mGains)
124     {
125         std::fill(std::begin(gain.Current), std::end(gain.Current), 0.0f);
126         std::fill(std::begin(gain.Target), std::end(gain.Target), 0.0f);
127     }
128 }
129
130 void FshifterState::update(const ContextBase *context, const EffectSlot *slot,
131     const EffectProps *props, const EffectTarget target)
132 {
133     const DeviceBase *device{context->mDevice};
134
135     const float step{props->Fshifter.Frequency / static_cast<float>(device->Frequency)};
136     mPhaseStep[0] = mPhaseStep[1] = fastf2u(minf(step, 1.0f) * MixerFracOne);
137
138     switch(props->Fshifter.LeftDirection)
139     {
140     case FShifterDirection::Down:
141         mSign[0] = -1.0;
142         break;
143     case FShifterDirection::Up:
144         mSign[0] = 1.0;
145         break;
146     case FShifterDirection::Off:
147         mPhase[0]     = 0;
148         mPhaseStep[0] = 0;
149         break;
150     }
151
152     switch(props->Fshifter.RightDirection)
153     {
154     case FShifterDirection::Down:
155         mSign[1] = -1.0;
156         break;
157     case FShifterDirection::Up:
158         mSign[1] = 1.0;
159         break;
160     case FShifterDirection::Off:
161         mPhase[1]     = 0;
162         mPhaseStep[1] = 0;
163         break;
164     }
165
166     static constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2);
167     static constexpr auto lcoeffs_pw = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f});
168     static constexpr auto rcoeffs_pw = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f});
169     static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs({-inv_sqrt2, 0.0f, inv_sqrt2});
170     static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs({ inv_sqrt2, 0.0f, inv_sqrt2});
171     auto &lcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? lcoeffs_nrml : lcoeffs_pw;
172     auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw;
173
174     mOutTarget = target.Main->Buffer;
175     ComputePanGains(target.Main, lcoeffs.data(), slot->Gain, mGains[0].Target);
176     ComputePanGains(target.Main, rcoeffs.data(), slot->Gain, mGains[1].Target);
177 }
178
179 void FshifterState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
180 {
181     for(size_t base{0u};base < samplesToDo;)
182     {
183         size_t todo{minz(HilStep-mCount, samplesToDo-base)};
184
185         /* Fill FIFO buffer with samples data */
186         const size_t pos{mPos};
187         size_t count{mCount};
188         do {
189             mInFIFO[pos+count] = samplesIn[0][base];
190             mOutdata[base] = mOutFIFO[count];
191             ++base; ++count;
192         } while(--todo);
193         mCount = count;
194
195         /* Check whether FIFO buffer is filled */
196         if(mCount < HilStep) break;
197         mCount = 0;
198         mPos = (mPos+HilStep) & (HilSize-1);
199
200         /* Real signal windowing and store in Analytic buffer */
201         for(size_t src{mPos}, k{0u};src < HilSize;++src,++k)
202             mAnalytic[k] = mInFIFO[src]*gWindow.mData[k];
203         for(size_t src{0u}, k{HilSize-mPos};src < mPos;++src,++k)
204             mAnalytic[k] = mInFIFO[src]*gWindow.mData[k];
205
206         /* Processing signal by Discrete Hilbert Transform (analytical signal). */
207         complex_hilbert(mAnalytic);
208
209         /* Windowing and add to output accumulator */
210         for(size_t dst{mPos}, k{0u};dst < HilSize;++dst,++k)
211             mOutputAccum[dst] += 2.0/OversampleFactor*gWindow.mData[k]*mAnalytic[k];
212         for(size_t dst{0u}, k{HilSize-mPos};dst < mPos;++dst,++k)
213             mOutputAccum[dst] += 2.0/OversampleFactor*gWindow.mData[k]*mAnalytic[k];
214
215         /* Copy out the accumulated result, then clear for the next iteration. */
216         std::copy_n(mOutputAccum.cbegin() + mPos, HilStep, mOutFIFO.begin());
217         std::fill_n(mOutputAccum.begin() + mPos, HilStep, complex_d{});
218     }
219
220     /* Process frequency shifter using the analytic signal obtained. */
221     float *RESTRICT BufferOut{al::assume_aligned<16>(mBufferOut.data())};
222     for(size_t c{0};c < 2;++c)
223     {
224         const uint phase_step{mPhaseStep[c]};
225         uint phase_idx{mPhase[c]};
226         for(size_t k{0};k < samplesToDo;++k)
227         {
228             const double phase{phase_idx * (al::numbers::pi*2.0 / MixerFracOne)};
229             BufferOut[k] = static_cast<float>(mOutdata[k].real()*std::cos(phase) +
230                 mOutdata[k].imag()*std::sin(phase)*mSign[c]);
231
232             phase_idx += phase_step;
233             phase_idx &= MixerFracMask;
234         }
235         mPhase[c] = phase_idx;
236
237         /* Now, mix the processed sound data to the output. */
238         MixSamples({BufferOut, samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target,
239             maxz(samplesToDo, 512), 0);
240     }
241 }
242
243
244 struct FshifterStateFactory final : public EffectStateFactory {
245     al::intrusive_ptr<EffectState> create() override
246     { return al::intrusive_ptr<EffectState>{new FshifterState{}}; }
247 };
248
249 } // namespace
250
251 EffectStateFactory *FshifterStateFactory_getFactory()
252 {
253     static FshifterStateFactory FshifterFactory{};
254     return &FshifterFactory;
255 }