Use proper inheritence for the effect state objects
[openal-soft.git] / Alc / effects / fshifter.cpp
blob38fb54920c99eb449a8e4758c56a35c949152626
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.
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
21 #include "config.h"
23 #include <cmath>
24 #include <cstdlib>
25 #include <array>
26 #include <complex>
27 #include <algorithm>
29 #include "alMain.h"
30 #include "alcontext.h"
31 #include "alAuxEffectSlot.h"
32 #include "alError.h"
33 #include "alu.h"
35 #include "alcomplex.h"
37 namespace {
39 using complex_d = std::complex<double>;
41 #define HIL_SIZE 1024
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;
58 return ret;
60 alignas(16) const std::array<ALdouble,HIL_SIZE> HannWindow = InitHannWindow();
63 struct ALfshifterState final : public EffectState {
64 /* Effect parameters */
65 ALsizei mCount{};
66 ALsizei mPhaseStep{};
67 ALsizei mPhase{};
68 ALdouble mLdSign{};
70 /*Effects buffers*/
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;
95 mPhaseStep = 0;
96 mPhase = 0;
97 mLdSign = 1.0;
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);
107 return AL_TRUE;
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:
120 mLdSign = -1.0;
121 break;
123 case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
124 mLdSign = 1.0;
125 break;
127 case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
128 mPhase = 0;
129 mPhaseStep = 0;
130 break;
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;
142 ALsizei j, k, base;
144 for(base = 0;base < SamplesToDo;)
146 ALsizei todo = mini(HIL_SIZE-mCount, SamplesToDo-base);
148 ASSUME(todo > 0);
150 /* Fill FIFO buffer with samples data */
151 k = mCount;
152 for(j = 0;j < todo;j++,k++)
154 mInFIFO[k] = SamplesIn[0][base+j];
155 mOutdata[base+j] = mOutFIFO[k-FIFO_LATENCY];
157 mCount += todo;
158 base += todo;
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);
202 } // namespace
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;
221 switch(param)
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;
227 break;
229 default:
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;
242 switch(param)
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;
248 break;
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;
254 break;
256 default:
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;
268 switch(param)
270 case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
271 *val = props->Fshifter.LeftDirection;
272 break;
273 case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
274 *val = props->Fshifter.RightDirection;
275 break;
276 default:
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;
289 switch(param)
291 case AL_FREQUENCY_SHIFTER_FREQUENCY:
292 *val = props->Fshifter.Frequency;
293 break;
295 default:
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);