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
30 #include "alc/effects/base.h"
31 #include "alcomplex.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/mixer.h"
42 #include "core/mixer/defs.h"
43 #include "intrusive_ptr.h"
44 #include "opthelpers.h"
50 using uint
= unsigned int;
51 using complex_d
= std::complex<double>;
53 constexpr size_t HilSize
{1024};
54 constexpr size_t HilHalfSize
{HilSize
>> 1};
55 constexpr size_t OversampleFactor
{4};
57 static_assert(HilSize
%OversampleFactor
== 0, "Factor must be a clean divisor of the size");
58 constexpr size_t HilStep
{HilSize
/ OversampleFactor
};
60 /* Define a Hann window, used to filter the HIL input and output. */
62 alignas(16) std::array
<double,HilSize
> mData
{};
66 /* Create lookup table of the Hann window for the desired size. */
67 for(size_t i
{0};i
< HilHalfSize
;i
++)
69 constexpr double scale
{al::numbers::pi
/ double{HilSize
}};
70 const double val
{std::sin((static_cast<double>(i
)+0.5) * scale
)};
71 mData
[i
] = mData
[HilSize
-1-i
] = val
* val
;
75 const Windower gWindow
{};
78 struct FshifterState final
: public EffectState
{
79 /* Effect parameters */
82 std::array
<uint
,2> mPhaseStep
{};
83 std::array
<uint
,2> mPhase
{};
84 std::array
<double,2> mSign
{};
87 std::array
<double,HilSize
> mInFIFO
{};
88 std::array
<complex_d
,HilStep
> mOutFIFO
{};
89 std::array
<complex_d
,HilSize
> mOutputAccum
{};
90 std::array
<complex_d
,HilSize
> mAnalytic
{};
91 std::array
<complex_d
,BufferLineSize
> mOutdata
{};
93 alignas(16) FloatBufferLine mBufferOut
{};
95 /* Effect gains for each output channel */
97 std::array
<float,MaxAmbiChannels
> Current
{};
98 std::array
<float,MaxAmbiChannels
> Target
{};
100 std::array
<OutGains
,2> mGains
;
103 void deviceUpdate(const DeviceBase
*device
, const BufferStorage
*buffer
) override
;
104 void update(const ContextBase
*context
, const EffectSlot
*slot
, const EffectProps
*props
,
105 const EffectTarget target
) override
;
106 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
,
107 const al::span
<FloatBufferLine
> samplesOut
) override
;
110 void FshifterState::deviceUpdate(const DeviceBase
*, const BufferStorage
*)
112 /* (Re-)initializing parameters and clear the buffers. */
114 mPos
= HilSize
- HilStep
;
120 mOutFIFO
.fill(complex_d
{});
121 mOutputAccum
.fill(complex_d
{});
122 mAnalytic
.fill(complex_d
{});
124 for(auto &gain
: mGains
)
126 gain
.Current
.fill(0.0f
);
127 gain
.Target
.fill(0.0f
);
131 void FshifterState::update(const ContextBase
*context
, const EffectSlot
*slot
,
132 const EffectProps
*props_
, const EffectTarget target
)
134 auto &props
= std::get
<FshifterProps
>(*props_
);
135 const DeviceBase
*device
{context
->mDevice
};
137 const float step
{props
.Frequency
/ static_cast<float>(device
->Frequency
)};
138 mPhaseStep
[0] = mPhaseStep
[1] = fastf2u(std::min(step
, 1.0f
) * MixerFracOne
);
140 switch(props
.LeftDirection
)
142 case FShifterDirection::Down
:
145 case FShifterDirection::Up
:
148 case FShifterDirection::Off
:
154 switch(props
.RightDirection
)
156 case FShifterDirection::Down
:
159 case FShifterDirection::Up
:
162 case FShifterDirection::Off
:
168 static constexpr auto inv_sqrt2
= static_cast<float>(1.0 / al::numbers::sqrt2
);
169 static constexpr auto lcoeffs_pw
= CalcDirectionCoeffs(std::array
{-1.0f
, 0.0f
, 0.0f
});
170 static constexpr auto rcoeffs_pw
= CalcDirectionCoeffs(std::array
{ 1.0f
, 0.0f
, 0.0f
});
171 static constexpr auto lcoeffs_nrml
= CalcDirectionCoeffs(std::array
{-inv_sqrt2
, 0.0f
, inv_sqrt2
});
172 static constexpr auto rcoeffs_nrml
= CalcDirectionCoeffs(std::array
{ inv_sqrt2
, 0.0f
, inv_sqrt2
});
173 auto &lcoeffs
= (device
->mRenderMode
!= RenderMode::Pairwise
) ? lcoeffs_nrml
: lcoeffs_pw
;
174 auto &rcoeffs
= (device
->mRenderMode
!= RenderMode::Pairwise
) ? rcoeffs_nrml
: rcoeffs_pw
;
176 mOutTarget
= target
.Main
->Buffer
;
177 ComputePanGains(target
.Main
, lcoeffs
, slot
->Gain
, mGains
[0].Target
);
178 ComputePanGains(target
.Main
, rcoeffs
, slot
->Gain
, mGains
[1].Target
);
181 void FshifterState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
183 for(size_t base
{0u};base
< samplesToDo
;)
185 size_t todo
{std::min(HilStep
-mCount
, samplesToDo
-base
)};
187 /* Fill FIFO buffer with samples data */
188 const size_t pos
{mPos
};
189 size_t count
{mCount
};
191 mInFIFO
[pos
+count
] = samplesIn
[0][base
];
192 mOutdata
[base
] = mOutFIFO
[count
];
197 /* Check whether FIFO buffer is filled */
198 if(mCount
< HilStep
) break;
200 mPos
= (mPos
+HilStep
) & (HilSize
-1);
202 /* Real signal windowing and store in Analytic buffer */
203 for(size_t src
{mPos
}, k
{0u};src
< HilSize
;++src
,++k
)
204 mAnalytic
[k
] = mInFIFO
[src
]*gWindow
.mData
[k
];
205 for(size_t src
{0u}, k
{HilSize
-mPos
};src
< mPos
;++src
,++k
)
206 mAnalytic
[k
] = mInFIFO
[src
]*gWindow
.mData
[k
];
208 /* Processing signal by Discrete Hilbert Transform (analytical signal). */
209 complex_hilbert(mAnalytic
);
211 /* Windowing and add to output accumulator */
212 for(size_t dst
{mPos
}, k
{0u};dst
< HilSize
;++dst
,++k
)
213 mOutputAccum
[dst
] += 2.0/OversampleFactor
*gWindow
.mData
[k
]*mAnalytic
[k
];
214 for(size_t dst
{0u}, k
{HilSize
-mPos
};dst
< mPos
;++dst
,++k
)
215 mOutputAccum
[dst
] += 2.0/OversampleFactor
*gWindow
.mData
[k
]*mAnalytic
[k
];
217 /* Copy out the accumulated result, then clear for the next iteration. */
218 std::copy_n(mOutputAccum
.cbegin() + mPos
, HilStep
, mOutFIFO
.begin());
219 std::fill_n(mOutputAccum
.begin() + mPos
, HilStep
, complex_d
{});
222 /* Process frequency shifter using the analytic signal obtained. */
223 for(size_t c
{0};c
< 2;++c
)
225 const double sign
{mSign
[c
]};
226 const uint phase_step
{mPhaseStep
[c
]};
227 uint phase_idx
{mPhase
[c
]};
228 std::transform(mOutdata
.cbegin(), mOutdata
.cbegin()+samplesToDo
, mBufferOut
.begin(),
229 [&phase_idx
,phase_step
,sign
](const complex_d
&in
) -> float
231 const double phase
{phase_idx
* (al::numbers::pi
*2.0 / MixerFracOne
)};
232 const auto out
= static_cast<float>(in
.real()*std::cos(phase
) +
233 in
.imag()*std::sin(phase
)*sign
);
235 phase_idx
+= phase_step
;
236 phase_idx
&= MixerFracMask
;
239 mPhase
[c
] = phase_idx
;
241 /* Now, mix the processed sound data to the output. */
242 MixSamples(al::span
{mBufferOut
}.first(samplesToDo
), samplesOut
, mGains
[c
].Current
,
243 mGains
[c
].Target
, std::max(samplesToDo
, 512_uz
), 0);
248 struct FshifterStateFactory final
: public EffectStateFactory
{
249 al::intrusive_ptr
<EffectState
> create() override
250 { return al::intrusive_ptr
<EffectState
>{new FshifterState
{}}; }
255 EffectStateFactory
*FshifterStateFactory_getFactory()
257 static FshifterStateFactory FshifterFactory
{};
258 return &FshifterFactory
;