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"
40 using complex_d
= std::complex<double>;
42 #define STFT_SIZE 1024
43 #define STFT_HALF_SIZE (STFT_SIZE>>1)
44 #define OVERSAMP (1<<2)
46 #define STFT_STEP (STFT_SIZE / OVERSAMP)
47 #define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
49 inline int double2int(double d
)
51 #if ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
52 !defined(__SSE2_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2)
61 sign
= (conv
.i64
>>63) | 1;
62 shift
= ((conv
.i64
>>52)&0x7ff) - (1023+52);
65 if(UNLIKELY(shift
>= 63 || shift
< -52))
68 mant
= (conv
.i64
&I64(0xfffffffffffff)) | I64(0x10000000000000);
70 return (ALint
)(mant
>> -shift
) * sign
;
71 return (ALint
)(mant
<< shift
) * sign
;
79 /* Define a Hann window, used to filter the STFT input and output. */
80 /* Making this constexpr seems to require C++14. */
81 std::array
<ALdouble
,STFT_SIZE
> InitHannWindow(void)
83 std::array
<ALdouble
,STFT_SIZE
> ret
;
84 /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
85 for(ALsizei i
{0};i
< STFT_SIZE
>>1;i
++)
87 ALdouble val
= std::sin(M_PI
* (ALdouble
)i
/ (ALdouble
)(STFT_SIZE
-1));
88 ret
[i
] = ret
[STFT_SIZE
-1-i
] = val
* val
;
92 alignas(16) const std::array
<ALdouble
,STFT_SIZE
> HannWindow
= InitHannWindow();
100 struct ALfrequencyDomain
{
106 /* Converts complex to ALphasor */
107 inline ALphasor
rect2polar(const complex_d
&number
)
110 polar
.Amplitude
= std::abs(number
);
111 polar
.Phase
= std::arg(number
);
115 /* Converts ALphasor to complex */
116 inline complex_d
polar2rect(const ALphasor
&number
)
117 { return std::polar
<double>(number
.Amplitude
, number
.Phase
); }
120 struct ALpshifterState final
: public ALeffectState
{
121 /* Effect parameters */
123 ALsizei mPitchShiftI
;
127 /* Effects buffers */
128 ALfloat mInFIFO
[STFT_SIZE
];
129 ALfloat mOutFIFO
[STFT_STEP
];
130 ALdouble mLastPhase
[STFT_HALF_SIZE
+1];
131 ALdouble mSumPhase
[STFT_HALF_SIZE
+1];
132 ALdouble mOutputAccum
[STFT_SIZE
];
134 complex_d mFFTbuffer
[STFT_SIZE
];
136 ALfrequencyDomain mAnalysis_buffer
[STFT_HALF_SIZE
+1];
137 ALfrequencyDomain mSyntesis_buffer
[STFT_HALF_SIZE
+1];
139 alignas(16) ALfloat mBufferOut
[BUFFERSIZE
];
141 /* Effect gains for each output channel */
142 ALfloat mCurrentGains
[MAX_OUTPUT_CHANNELS
];
143 ALfloat mTargetGains
[MAX_OUTPUT_CHANNELS
];
146 static ALvoid
ALpshifterState_Destruct(ALpshifterState
*state
);
147 static ALboolean
ALpshifterState_deviceUpdate(ALpshifterState
*state
, ALCdevice
*device
);
148 static ALvoid
ALpshifterState_update(ALpshifterState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
);
149 static ALvoid
ALpshifterState_process(ALpshifterState
*state
, ALsizei SamplesToDo
, const ALfloat (*RESTRICT SamplesIn
)[BUFFERSIZE
], ALfloat (*RESTRICT SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
150 DECLARE_DEFAULT_ALLOCATORS(ALpshifterState
)
152 DEFINE_ALEFFECTSTATE_VTABLE(ALpshifterState
);
154 void ALpshifterState_Construct(ALpshifterState
*state
)
156 new (state
) ALpshifterState
{};
157 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
158 SET_VTABLE2(ALpshifterState
, ALeffectState
, state
);
161 ALvoid
ALpshifterState_Destruct(ALpshifterState
*state
)
163 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
164 state
->~ALpshifterState();
167 ALboolean
ALpshifterState_deviceUpdate(ALpshifterState
*state
, ALCdevice
*device
)
169 /* (Re-)initializing parameters and clear the buffers. */
170 state
->mCount
= FIFO_LATENCY
;
171 state
->mPitchShiftI
= FRACTIONONE
;
172 state
->mPitchShift
= 1.0f
;
173 state
->mFreqPerBin
= device
->Frequency
/ (ALfloat
)STFT_SIZE
;
175 std::fill(std::begin(state
->mInFIFO
), std::end(state
->mInFIFO
), 0.0f
);
176 std::fill(std::begin(state
->mOutFIFO
), std::end(state
->mOutFIFO
), 0.0f
);
177 std::fill(std::begin(state
->mLastPhase
), std::end(state
->mLastPhase
), 0.0);
178 std::fill(std::begin(state
->mSumPhase
), std::end(state
->mSumPhase
), 0.0);
179 std::fill(std::begin(state
->mOutputAccum
), std::end(state
->mOutputAccum
), 0.0);
180 std::fill(std::begin(state
->mFFTbuffer
), std::end(state
->mFFTbuffer
), complex_d
{});
181 std::fill(std::begin(state
->mAnalysis_buffer
), std::end(state
->mAnalysis_buffer
), ALfrequencyDomain
{});
182 std::fill(std::begin(state
->mSyntesis_buffer
), std::end(state
->mSyntesis_buffer
), ALfrequencyDomain
{});
184 std::fill(std::begin(state
->mCurrentGains
), std::end(state
->mCurrentGains
), 0.0f
);
185 std::fill(std::begin(state
->mTargetGains
), std::end(state
->mTargetGains
), 0.0f
);
190 ALvoid
ALpshifterState_update(ALpshifterState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
)
192 const ALCdevice
*device
= context
->Device
;
193 ALfloat coeffs
[MAX_AMBI_COEFFS
];
196 pitch
= std::pow(2.0f
,
197 (ALfloat
)(props
->Pshifter
.CoarseTune
*100 + props
->Pshifter
.FineTune
) / 1200.0f
199 state
->mPitchShiftI
= fastf2i(pitch
*FRACTIONONE
);
200 state
->mPitchShift
= state
->mPitchShiftI
* (1.0f
/FRACTIONONE
);
202 CalcAngleCoeffs(0.0f
, 0.0f
, 0.0f
, coeffs
);
203 ComputePanGains(&device
->Dry
, coeffs
, slot
->Params
.Gain
, state
->mTargetGains
);
206 ALvoid
ALpshifterState_process(ALpshifterState
*state
, ALsizei SamplesToDo
, const ALfloat (*RESTRICT SamplesIn
)[BUFFERSIZE
], ALfloat (*RESTRICT SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
208 /* Pitch shifter engine based on the work of Stephan Bernsee.
209 * http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
212 static constexpr ALdouble expected
{M_PI
*2.0 / OVERSAMP
};
213 const ALdouble freq_per_bin
{state
->mFreqPerBin
};
214 ALfloat
*RESTRICT bufferOut
{state
->mBufferOut
};
215 ALsizei count
{state
->mCount
};
217 for(ALsizei i
{0};i
< SamplesToDo
;)
220 /* Fill FIFO buffer with samples data */
221 state
->mInFIFO
[count
] = SamplesIn
[0][i
];
222 bufferOut
[i
] = state
->mOutFIFO
[count
- FIFO_LATENCY
];
225 } while(++i
< SamplesToDo
&& count
< STFT_SIZE
);
227 /* Check whether FIFO buffer is filled */
228 if(count
< STFT_SIZE
) break;
229 count
= FIFO_LATENCY
;
231 /* Real signal windowing and store in FFTbuffer */
232 for(ALsizei k
{0};k
< STFT_SIZE
;k
++)
234 state
->mFFTbuffer
[k
].real(state
->mInFIFO
[k
] * HannWindow
[k
]);
235 state
->mFFTbuffer
[k
].imag(0.0);
239 /* Apply FFT to FFTbuffer data */
240 complex_fft(state
->mFFTbuffer
, STFT_SIZE
, -1.0);
242 /* Analyze the obtained data. Since the real FFT is symmetric, only
243 * STFT_HALF_SIZE+1 samples are needed.
245 for(ALsizei k
{0};k
< STFT_HALF_SIZE
+1;k
++)
247 /* Compute amplitude and phase */
248 ALphasor component
{rect2polar(state
->mFFTbuffer
[k
])};
250 /* Compute phase difference and subtract expected phase difference */
251 double tmp
{(component
.Phase
- state
->mLastPhase
[k
]) - k
*expected
};
253 /* Map delta phase into +/- Pi interval */
254 int qpd
{double2int(tmp
/ M_PI
)};
255 tmp
-= M_PI
* (qpd
+ (qpd
%2));
257 /* Get deviation from bin frequency from the +/- Pi interval */
260 /* Compute the k-th partials' true frequency, twice the amplitude
261 * for maintain the gain (because half of bins are used) and store
262 * amplitude and true frequency in analysis buffer.
264 state
->mAnalysis_buffer
[k
].Amplitude
= 2.0 * component
.Amplitude
;
265 state
->mAnalysis_buffer
[k
].Frequency
= (k
+ tmp
) * freq_per_bin
;
267 /* Store actual phase[k] for the calculations in the next frame*/
268 state
->mLastPhase
[k
] = component
.Phase
;
273 for(ALsizei k
{0};k
< STFT_HALF_SIZE
+1;k
++)
275 state
->mSyntesis_buffer
[k
].Amplitude
= 0.0;
276 state
->mSyntesis_buffer
[k
].Frequency
= 0.0;
279 for(ALsizei k
{0};k
< STFT_HALF_SIZE
+1;k
++)
281 ALsizei j
{(k
*state
->mPitchShiftI
) >> FRACTIONBITS
};
282 if(j
>= STFT_HALF_SIZE
+1) break;
284 state
->mSyntesis_buffer
[j
].Amplitude
+= state
->mAnalysis_buffer
[k
].Amplitude
;
285 state
->mSyntesis_buffer
[j
].Frequency
= state
->mAnalysis_buffer
[k
].Frequency
*
290 /* Synthesis the processing data */
291 for(ALsizei k
{0};k
< STFT_HALF_SIZE
+1;k
++)
296 /* Compute bin deviation from scaled freq */
297 tmp
= state
->mSyntesis_buffer
[k
].Frequency
/freq_per_bin
- k
;
299 /* Calculate actual delta phase and accumulate it to get bin phase */
300 state
->mSumPhase
[k
] += (k
+ tmp
) * expected
;
302 component
.Amplitude
= state
->mSyntesis_buffer
[k
].Amplitude
;
303 component
.Phase
= state
->mSumPhase
[k
];
305 /* Compute phasor component to cartesian complex number and storage it into FFTbuffer*/
306 state
->mFFTbuffer
[k
] = polar2rect(component
);
308 /* zero negative frequencies for recontruct a real signal */
309 for(ALsizei k
{STFT_HALF_SIZE
+1};k
< STFT_SIZE
;k
++)
310 state
->mFFTbuffer
[k
] = complex_d
{};
312 /* Apply iFFT to buffer data */
313 complex_fft(state
->mFFTbuffer
, STFT_SIZE
, 1.0);
315 /* Windowing and add to output */
316 for(ALsizei k
{0};k
< STFT_SIZE
;k
++)
317 state
->mOutputAccum
[k
] += HannWindow
[k
] * state
->mFFTbuffer
[k
].real() /
318 (0.5 * STFT_HALF_SIZE
* OVERSAMP
);
320 /* Shift accumulator, input & output FIFO */
322 for(k
= 0;k
< STFT_STEP
;k
++) state
->mOutFIFO
[k
] = (ALfloat
)state
->mOutputAccum
[k
];
323 for(j
= 0;k
< STFT_SIZE
;k
++,j
++) state
->mOutputAccum
[j
] = state
->mOutputAccum
[k
];
324 for(;j
< STFT_SIZE
;j
++) state
->mOutputAccum
[j
] = 0.0;
325 for(k
= 0;k
< FIFO_LATENCY
;k
++)
326 state
->mInFIFO
[k
] = state
->mInFIFO
[k
+STFT_STEP
];
328 state
->mCount
= count
;
330 /* Now, mix the processed sound data to the output. */
331 MixSamples(bufferOut
, NumChannels
, SamplesOut
, state
->mCurrentGains
, state
->mTargetGains
,
332 maxi(SamplesToDo
, 512), 0, SamplesToDo
);
337 struct PshifterStateFactory final
: public EffectStateFactory
{
338 ALeffectState
*create() override
;
341 ALeffectState
*PshifterStateFactory::create()
343 ALpshifterState
*state
;
344 NEW_OBJ0(state
, ALpshifterState
)();
348 EffectStateFactory
*PshifterStateFactory_getFactory(void)
350 static PshifterStateFactory PshifterFactory
{};
351 return &PshifterFactory
;
355 void ALpshifter_setParamf(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALfloat
UNUSED(val
))
357 alSetError( context
, AL_INVALID_ENUM
, "Invalid pitch shifter float property 0x%04x", param
);
360 void ALpshifter_setParamfv(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, const ALfloat
*UNUSED(vals
))
362 alSetError( context
, AL_INVALID_ENUM
, "Invalid pitch shifter float-vector property 0x%04x", param
);
365 void ALpshifter_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
367 ALeffectProps
*props
= &effect
->Props
;
370 case AL_PITCH_SHIFTER_COARSE_TUNE
:
371 if(!(val
>= AL_PITCH_SHIFTER_MIN_COARSE_TUNE
&& val
<= AL_PITCH_SHIFTER_MAX_COARSE_TUNE
))
372 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Pitch shifter coarse tune out of range");
373 props
->Pshifter
.CoarseTune
= val
;
376 case AL_PITCH_SHIFTER_FINE_TUNE
:
377 if(!(val
>= AL_PITCH_SHIFTER_MIN_FINE_TUNE
&& val
<= AL_PITCH_SHIFTER_MAX_FINE_TUNE
))
378 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Pitch shifter fine tune out of range");
379 props
->Pshifter
.FineTune
= val
;
383 alSetError(context
, AL_INVALID_ENUM
, "Invalid pitch shifter integer property 0x%04x", param
);
386 void ALpshifter_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
388 ALpshifter_setParami(effect
, context
, param
, vals
[0]);
391 void ALpshifter_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
393 const ALeffectProps
*props
= &effect
->Props
;
396 case AL_PITCH_SHIFTER_COARSE_TUNE
:
397 *val
= (ALint
)props
->Pshifter
.CoarseTune
;
399 case AL_PITCH_SHIFTER_FINE_TUNE
:
400 *val
= (ALint
)props
->Pshifter
.FineTune
;
404 alSetError(context
, AL_INVALID_ENUM
, "Invalid pitch shifter integer property 0x%04x", param
);
407 void ALpshifter_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
409 ALpshifter_getParami(effect
, context
, param
, vals
);
412 void ALpshifter_getParamf(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALfloat
*UNUSED(val
))
414 alSetError(context
, AL_INVALID_ENUM
, "Invalid pitch shifter float property 0x%04x", param
);
417 void ALpshifter_getParamfv(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALfloat
*UNUSED(vals
))
419 alSetError(context
, AL_INVALID_ENUM
, "Invalid pitch shifter float vector-property 0x%04x", param
);
422 DEFINE_ALEFFECT_VTABLE(ALpshifter
);