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 "alcontext.h"
31 #include "alAuxEffectSlot.h"
35 #include "alcomplex.h"
39 using complex_d
= std::complex<double>;
42 #define OVERSAMP (1<<2)
44 #define HIL_STEP (HIL_SIZE / OVERSAMP)
45 #define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
47 /* Define a Hann window, used to filter the HIL input and output. */
48 /* Making this constexpr seems to require C++14. */
49 std::array
<ALdouble
,HIL_SIZE
> InitHannWindow(void)
51 std::array
<ALdouble
,HIL_SIZE
> ret
;
52 /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
53 for(ALsizei i
{0};i
< HIL_SIZE
>>1;i
++)
55 ALdouble val
= std::sin(M_PI
* (ALdouble
)i
/ (ALdouble
)(HIL_SIZE
-1));
56 ret
[i
] = ret
[HIL_SIZE
-1-i
] = val
* val
;
60 alignas(16) const std::array
<ALdouble
,HIL_SIZE
> HannWindow
= InitHannWindow();
63 struct ALfshifterState final
: public EffectState
{
64 /* Effect parameters */
71 ALfloat mInFIFO
[HIL_SIZE
]{};
72 complex_d mOutFIFO
[HIL_SIZE
]{};
73 complex_d mOutputAccum
[HIL_SIZE
]{};
74 complex_d mAnalytic
[HIL_SIZE
]{};
75 complex_d mOutdata
[BUFFERSIZE
]{};
77 alignas(16) ALfloat mBufferOut
[BUFFERSIZE
]{};
79 /* Effect gains for each output channel */
80 ALfloat mCurrentGains
[MAX_OUTPUT_CHANNELS
]{};
81 ALfloat mTargetGains
[MAX_OUTPUT_CHANNELS
]{};
84 ALboolean
deviceUpdate(ALCdevice
*device
) override
;
85 void update(const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
) override
;
86 void process(ALsizei samplesToDo
, const ALfloat (*RESTRICT samplesIn
)[BUFFERSIZE
], ALfloat (*RESTRICT samplesOut
)[BUFFERSIZE
], ALsizei numChannels
) override
;
88 DEF_NEWDEL(ALfshifterState
)
91 ALboolean
ALfshifterState::deviceUpdate(ALCdevice
*UNUSED(device
))
93 /* (Re-)initializing parameters and clear the buffers. */
94 mCount
= FIFO_LATENCY
;
99 std::fill(std::begin(mInFIFO
), std::end(mInFIFO
), 0.0f
);
100 std::fill(std::begin(mOutFIFO
), std::end(mOutFIFO
), complex_d
{});
101 std::fill(std::begin(mOutputAccum
), std::end(mOutputAccum
), complex_d
{});
102 std::fill(std::begin(mAnalytic
), std::end(mAnalytic
), complex_d
{});
104 std::fill(std::begin(mCurrentGains
), std::end(mCurrentGains
), 0.0f
);
105 std::fill(std::begin(mTargetGains
), std::end(mTargetGains
), 0.0f
);
110 void ALfshifterState::update(const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
)
112 const ALCdevice
*device
{context
->Device
};
114 ALfloat step
{props
->Fshifter
.Frequency
/ (ALfloat
)device
->Frequency
};
115 mPhaseStep
= fastf2i(minf(step
, 0.5f
) * FRACTIONONE
);
117 switch(props
->Fshifter
.LeftDirection
)
119 case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN
:
123 case AL_FREQUENCY_SHIFTER_DIRECTION_UP
:
127 case AL_FREQUENCY_SHIFTER_DIRECTION_OFF
:
133 ALfloat coeffs
[MAX_AMBI_COEFFS
];
134 CalcAngleCoeffs(0.0f
, 0.0f
, 0.0f
, coeffs
);
135 ComputePanGains(&device
->Dry
, coeffs
, slot
->Params
.Gain
, mTargetGains
);
138 void ALfshifterState::process(ALsizei SamplesToDo
, const ALfloat (*RESTRICT SamplesIn
)[BUFFERSIZE
], ALfloat (*RESTRICT SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
140 static const complex_d complex_zero
{0.0, 0.0};
141 ALfloat
*RESTRICT BufferOut
= mBufferOut
;
144 for(base
= 0;base
< SamplesToDo
;)
146 ALsizei todo
= mini(HIL_SIZE
-mCount
, SamplesToDo
-base
);
150 /* Fill FIFO buffer with samples data */
152 for(j
= 0;j
< todo
;j
++,k
++)
154 mInFIFO
[k
] = SamplesIn
[0][base
+j
];
155 mOutdata
[base
+j
] = mOutFIFO
[k
-FIFO_LATENCY
];
160 /* Check whether FIFO buffer is filled */
161 if(mCount
< HIL_SIZE
) continue;
162 mCount
= FIFO_LATENCY
;
164 /* Real signal windowing and store in Analytic buffer */
165 for(k
= 0;k
< HIL_SIZE
;k
++)
167 mAnalytic
[k
].real(mInFIFO
[k
] * HannWindow
[k
]);
168 mAnalytic
[k
].imag(0.0);
171 /* Processing signal by Discrete Hilbert Transform (analytical signal). */
172 complex_hilbert(mAnalytic
, HIL_SIZE
);
174 /* Windowing and add to output accumulator */
175 for(k
= 0;k
< HIL_SIZE
;k
++)
176 mOutputAccum
[k
] += 2.0/OVERSAMP
*HannWindow
[k
]*mAnalytic
[k
];
178 /* Shift accumulator, input & output FIFO */
179 for(k
= 0;k
< HIL_STEP
;k
++) mOutFIFO
[k
] = mOutputAccum
[k
];
180 for(j
= 0;k
< HIL_SIZE
;k
++,j
++) mOutputAccum
[j
] = mOutputAccum
[k
];
181 for(;j
< HIL_SIZE
;j
++) mOutputAccum
[j
] = complex_zero
;
182 for(k
= 0;k
< FIFO_LATENCY
;k
++)
183 mInFIFO
[k
] = mInFIFO
[k
+HIL_STEP
];
186 /* Process frequency shifter using the analytic signal obtained. */
187 for(k
= 0;k
< SamplesToDo
;k
++)
189 double phase
= mPhase
* ((1.0/FRACTIONONE
) * 2.0*M_PI
);
190 BufferOut
[k
] = (float)(mOutdata
[k
].real()*std::cos(phase
) +
191 mOutdata
[k
].imag()*std::sin(phase
)*mLdSign
);
193 mPhase
+= mPhaseStep
;
194 mPhase
&= FRACTIONMASK
;
197 /* Now, mix the processed sound data to the output. */
198 MixSamples(BufferOut
, NumChannels
, SamplesOut
, mCurrentGains
, mTargetGains
,
199 maxi(SamplesToDo
, 512), 0, SamplesToDo
);
204 struct FshifterStateFactory final
: public EffectStateFactory
{
205 EffectState
*create() override
;
208 EffectState
*FshifterStateFactory::create()
209 { return new ALfshifterState
{}; }
211 EffectStateFactory
*FshifterStateFactory_getFactory(void)
213 static FshifterStateFactory FshifterFactory
{};
214 return &FshifterFactory
;
218 void ALfshifter_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
220 ALeffectProps
*props
= &effect
->Props
;
223 case AL_FREQUENCY_SHIFTER_FREQUENCY
:
224 if(!(val
>= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY
&& val
<= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY
))
225 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Frequency shifter frequency out of range");
226 props
->Fshifter
.Frequency
= val
;
230 alSetError(context
, AL_INVALID_ENUM
, "Invalid frequency shifter float property 0x%04x", param
);
234 void ALfshifter_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
236 ALfshifter_setParamf(effect
, context
, param
, vals
[0]);
239 void ALfshifter_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
241 ALeffectProps
*props
= &effect
->Props
;
244 case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION
:
245 if(!(val
>= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION
&& val
<= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION
))
246 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Frequency shifter left direction out of range");
247 props
->Fshifter
.LeftDirection
= val
;
250 case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION
:
251 if(!(val
>= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION
&& val
<= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION
))
252 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Frequency shifter right direction out of range");
253 props
->Fshifter
.RightDirection
= val
;
257 alSetError(context
, AL_INVALID_ENUM
, "Invalid frequency shifter integer property 0x%04x", param
);
260 void ALfshifter_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
262 ALfshifter_setParami(effect
, context
, param
, vals
[0]);
265 void ALfshifter_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
267 const ALeffectProps
*props
= &effect
->Props
;
270 case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION
:
271 *val
= props
->Fshifter
.LeftDirection
;
273 case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION
:
274 *val
= props
->Fshifter
.RightDirection
;
277 alSetError(context
, AL_INVALID_ENUM
, "Invalid frequency shifter integer property 0x%04x", param
);
280 void ALfshifter_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
282 ALfshifter_getParami(effect
, context
, param
, vals
);
285 void ALfshifter_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
288 const ALeffectProps
*props
= &effect
->Props
;
291 case AL_FREQUENCY_SHIFTER_FREQUENCY
:
292 *val
= props
->Fshifter
.Frequency
;
296 alSetError(context
, AL_INVALID_ENUM
, "Invalid frequency shifter float property 0x%04x", param
);
301 void ALfshifter_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
303 ALfshifter_getParamf(effect
, context
, param
, vals
);
306 DEFINE_ALEFFECT_VTABLE(ALfshifter
);