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
29 #include "al/auxeffectslot.h"
31 #include "alcontext.h"
34 #include "alcomplex.h"
38 using complex_d
= std::complex<double>;
41 #define OVERSAMP (1<<2)
43 #define HIL_STEP (HIL_SIZE / OVERSAMP)
44 #define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
46 /* Define a Hann window, used to filter the HIL input and output. */
47 /* Making this constexpr seems to require C++14. */
48 std::array
<ALdouble
,HIL_SIZE
> InitHannWindow()
50 std::array
<ALdouble
,HIL_SIZE
> ret
;
51 /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
52 for(size_t i
{0};i
< HIL_SIZE
>>1;i
++)
54 constexpr double scale
{al::MathDefs
<double>::Pi() / double{HIL_SIZE
-1}};
55 const double val
{std::sin(static_cast<double>(i
) * scale
)};
56 ret
[i
] = ret
[HIL_SIZE
-1-i
] = val
* val
;
60 alignas(16) const std::array
<ALdouble
,HIL_SIZE
> HannWindow
= InitHannWindow();
63 struct FshifterState final
: public EffectState
{
64 /* Effect parameters */
66 ALsizei mPhaseStep
[2]{};
72 ALfloat mInFIFO
[HIL_SIZE
]{};
73 complex_d mOutFIFO
[HIL_SIZE
]{};
74 complex_d mOutputAccum
[HIL_SIZE
]{};
75 complex_d mAnalytic
[HIL_SIZE
]{};
76 complex_d mOutdata
[BUFFERSIZE
]{};
78 alignas(16) ALfloat mBufferOut
[BUFFERSIZE
]{};
80 /* Effect gains for each output channel */
82 ALfloat Current
[MAX_OUTPUT_CHANNELS
]{};
83 ALfloat Target
[MAX_OUTPUT_CHANNELS
]{};
87 ALboolean
deviceUpdate(const ALCdevice
*device
) override
;
88 void update(const ALCcontext
*context
, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
) override
;
89 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
) override
;
91 DEF_NEWDEL(FshifterState
)
94 ALboolean
FshifterState::deviceUpdate(const ALCdevice
*)
96 /* (Re-)initializing parameters and clear the buffers. */
97 mCount
= FIFO_LATENCY
;
99 std::fill(std::begin(mPhaseStep
), std::end(mPhaseStep
), 0);
100 std::fill(std::begin(mPhase
), std::end(mPhase
), 0);
101 std::fill(std::begin(mSign
), std::end(mSign
), 1.0);
102 std::fill(std::begin(mInFIFO
), std::end(mInFIFO
), 0.0f
);
103 std::fill(std::begin(mOutFIFO
), std::end(mOutFIFO
), complex_d
{});
104 std::fill(std::begin(mOutputAccum
), std::end(mOutputAccum
), complex_d
{});
105 std::fill(std::begin(mAnalytic
), std::end(mAnalytic
), complex_d
{});
107 for(auto &gain
: mGains
)
109 std::fill(std::begin(gain
.Current
), std::end(gain
.Current
), 0.0f
);
110 std::fill(std::begin(gain
.Target
), std::end(gain
.Target
), 0.0f
);
116 void FshifterState::update(const ALCcontext
*context
, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
)
118 const ALCdevice
*device
{context
->mDevice
.get()};
120 ALfloat step
{props
->Fshifter
.Frequency
/ static_cast<ALfloat
>(device
->Frequency
)};
121 mPhaseStep
[0] = mPhaseStep
[1] = fastf2i(minf(step
, 0.5f
) * FRACTIONONE
);
123 switch(props
->Fshifter
.LeftDirection
)
125 case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN
:
129 case AL_FREQUENCY_SHIFTER_DIRECTION_UP
:
133 case AL_FREQUENCY_SHIFTER_DIRECTION_OFF
:
139 switch (props
->Fshifter
.RightDirection
)
141 case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN
:
145 case AL_FREQUENCY_SHIFTER_DIRECTION_UP
:
149 case AL_FREQUENCY_SHIFTER_DIRECTION_OFF
:
155 ALfloat coeffs
[2][MAX_AMBI_CHANNELS
];
156 CalcDirectionCoeffs({-1.0f
, 0.0f
, -1.0f
}, 0.0f
, coeffs
[0]);
157 CalcDirectionCoeffs({ 1.0f
, 0.0f
, -1.0f
}, 0.0f
, coeffs
[1]);
159 mOutTarget
= target
.Main
->Buffer
;
160 ComputePanGains(target
.Main
, coeffs
[0], slot
->Params
.Gain
, mGains
[0].Target
);
161 ComputePanGains(target
.Main
, coeffs
[1], slot
->Params
.Gain
, mGains
[1].Target
);
164 void FshifterState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
166 static constexpr complex_d complex_zero
{0.0, 0.0};
167 ALfloat
*RESTRICT BufferOut
= mBufferOut
;
170 for(size_t base
{0u};base
< samplesToDo
;)
172 const size_t todo
{minz(HIL_SIZE
-mCount
, samplesToDo
-base
)};
176 /* Fill FIFO buffer with samples data */
178 for(j
= 0;j
< todo
;j
++,k
++)
180 mInFIFO
[k
] = samplesIn
[0][base
+j
];
181 mOutdata
[base
+j
] = mOutFIFO
[k
-FIFO_LATENCY
];
186 /* Check whether FIFO buffer is filled */
187 if(mCount
< HIL_SIZE
) continue;
188 mCount
= FIFO_LATENCY
;
190 /* Real signal windowing and store in Analytic buffer */
191 for(k
= 0;k
< HIL_SIZE
;k
++)
193 mAnalytic
[k
].real(mInFIFO
[k
] * HannWindow
[k
]);
194 mAnalytic
[k
].imag(0.0);
197 /* Processing signal by Discrete Hilbert Transform (analytical signal). */
198 complex_hilbert(mAnalytic
);
200 /* Windowing and add to output accumulator */
201 for(k
= 0;k
< HIL_SIZE
;k
++)
202 mOutputAccum
[k
] += 2.0/OVERSAMP
*HannWindow
[k
]*mAnalytic
[k
];
204 /* Shift accumulator, input & output FIFO */
205 for(k
= 0;k
< HIL_STEP
;k
++) mOutFIFO
[k
] = mOutputAccum
[k
];
206 for(j
= 0;k
< HIL_SIZE
;k
++,j
++) mOutputAccum
[j
] = mOutputAccum
[k
];
207 for(;j
< HIL_SIZE
;j
++) mOutputAccum
[j
] = complex_zero
;
208 for(k
= 0;k
< FIFO_LATENCY
;k
++)
209 mInFIFO
[k
] = mInFIFO
[k
+HIL_STEP
];
212 /* Process frequency shifter using the analytic signal obtained. */
213 for(ALsizei c
{0};c
< 2;++c
)
215 for(k
= 0;k
< samplesToDo
;++k
)
217 double phase
= mPhase
[c
] * ((1.0 / FRACTIONONE
) * al::MathDefs
<double>::Tau());
218 BufferOut
[k
] = static_cast<float>(mOutdata
[k
].real()*std::cos(phase
) +
219 mOutdata
[k
].imag()*std::sin(phase
)*mSign
[c
]);
221 mPhase
[c
] += mPhaseStep
[c
];
222 mPhase
[c
] &= FRACTIONMASK
;
225 /* Now, mix the processed sound data to the output. */
226 MixSamples({BufferOut
, samplesToDo
}, samplesOut
, mGains
[c
].Current
, mGains
[c
].Target
,
227 maxz(samplesToDo
, 512), 0);
232 void Fshifter_setParamf(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat val
)
236 case AL_FREQUENCY_SHIFTER_FREQUENCY
:
237 if(!(val
>= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY
&& val
<= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY
))
238 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Frequency shifter frequency out of range");
239 props
->Fshifter
.Frequency
= val
;
243 context
->setError(AL_INVALID_ENUM
, "Invalid frequency shifter float property 0x%04x",
247 void Fshifter_setParamfv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
248 { Fshifter_setParamf(props
, context
, param
, vals
[0]); }
250 void Fshifter_setParami(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint val
)
254 case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION
:
255 if(!(val
>= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION
&& val
<= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION
))
256 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Frequency shifter left direction out of range");
257 props
->Fshifter
.LeftDirection
= val
;
260 case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION
:
261 if(!(val
>= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION
&& val
<= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION
))
262 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Frequency shifter right direction out of range");
263 props
->Fshifter
.RightDirection
= val
;
267 context
->setError(AL_INVALID_ENUM
, "Invalid frequency shifter integer property 0x%04x",
271 void Fshifter_setParamiv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
272 { Fshifter_setParami(props
, context
, param
, vals
[0]); }
274 void Fshifter_getParami(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*val
)
278 case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION
:
279 *val
= props
->Fshifter
.LeftDirection
;
281 case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION
:
282 *val
= props
->Fshifter
.RightDirection
;
285 context
->setError(AL_INVALID_ENUM
, "Invalid frequency shifter integer property 0x%04x",
289 void Fshifter_getParamiv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
290 { Fshifter_getParami(props
, context
, param
, vals
); }
292 void Fshifter_getParamf(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
296 case AL_FREQUENCY_SHIFTER_FREQUENCY
:
297 *val
= props
->Fshifter
.Frequency
;
301 context
->setError(AL_INVALID_ENUM
, "Invalid frequency shifter float property 0x%04x",
305 void Fshifter_getParamfv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
306 { Fshifter_getParamf(props
, context
, param
, vals
); }
308 DEFINE_ALEFFECT_VTABLE(Fshifter
);
311 struct FshifterStateFactory final
: public EffectStateFactory
{
312 EffectState
*create() override
{ return new FshifterState
{}; }
313 EffectProps
getDefaultProps() const noexcept override
;
314 const EffectVtable
*getEffectVtable() const noexcept override
{ return &Fshifter_vtable
; }
317 EffectProps
FshifterStateFactory::getDefaultProps() const noexcept
320 props
.Fshifter
.Frequency
= AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY
;
321 props
.Fshifter
.LeftDirection
= AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION
;
322 props
.Fshifter
.RightDirection
= AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION
;
328 EffectStateFactory
*FshifterStateFactory_getFactory()
330 static FshifterStateFactory FshifterFactory
{};
331 return &FshifterFactory
;