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"
33 #include "alnumbers.h"
34 #include "alnumeric.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"
48 using uint
= unsigned int;
49 using complex_d
= std::complex<double>;
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
;
70 alignas(16) const std::array
<double,HIL_SIZE
> HannWindow
= InitHannWindow();
73 struct FshifterState final
: public EffectState
{
74 /* Effect parameters */
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 */
92 float Current
[MAX_OUTPUT_CHANNELS
]{};
93 float Target
[MAX_OUTPUT_CHANNELS
]{};
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. */
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
:
140 case FShifterDirection::Up
:
143 case FShifterDirection::Off
:
149 switch(props
->Fshifter
.RightDirection
)
151 case FShifterDirection::Down
:
154 case FShifterDirection::Up
:
157 case FShifterDirection::Off
:
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
};
181 mInFIFO
[pos
+count
] = samplesIn
[0][base
];
182 mOutdata
[base
] = mOutFIFO
[count
];
187 /* Check whether FIFO buffer is filled */
188 if(mCount
< HIL_STEP
) break;
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
{}}; }
243 EffectStateFactory
*FshifterStateFactory_getFactory()
245 static FshifterStateFactory FshifterFactory
{};
246 return &FshifterFactory
;