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
27 #include "alAuxEffectSlot.h"
30 #include "filters/defs.h"
33 #define STFT_SIZE 1024
34 #define STFT_HALF_SIZE (STFT_SIZE>>1)
35 #define OVERSAMP (1<<2)
37 #define STFT_STEP (STFT_SIZE / OVERSAMP)
38 #define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
40 typedef struct ALcomplex
{
45 typedef struct ALphasor
{
50 typedef struct ALFrequencyDomain
{
55 typedef struct ALpshifterState
{
56 DERIVE_FROM_TYPE(ALeffectState
);
58 /* Effect parameters */
64 ALfloat InFIFO
[STFT_SIZE
];
65 ALfloat OutFIFO
[STFT_STEP
];
66 ALfloat LastPhase
[STFT_HALF_SIZE
+1];
67 ALfloat SumPhase
[STFT_HALF_SIZE
+1];
68 ALfloat OutputAccum
[STFT_SIZE
];
70 ALcomplex FFTbuffer
[STFT_SIZE
];
72 ALfrequencyDomain Analysis_buffer
[STFT_HALF_SIZE
+1];
73 ALfrequencyDomain Syntesis_buffer
[STFT_HALF_SIZE
+1];
75 alignas(16) ALfloat BufferOut
[BUFFERSIZE
];
77 /* Effect gains for each output channel */
78 ALfloat CurrentGains
[MAX_OUTPUT_CHANNELS
];
79 ALfloat TargetGains
[MAX_OUTPUT_CHANNELS
];
82 static ALvoid
ALpshifterState_Destruct(ALpshifterState
*state
);
83 static ALboolean
ALpshifterState_deviceUpdate(ALpshifterState
*state
, ALCdevice
*device
);
84 static ALvoid
ALpshifterState_update(ALpshifterState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
);
85 static ALvoid
ALpshifterState_process(ALpshifterState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
);
86 DECLARE_DEFAULT_ALLOCATORS(ALpshifterState
)
88 DEFINE_ALEFFECTSTATE_VTABLE(ALpshifterState
);
91 /* Define a Hann window, used to filter the STFT input and output. */
92 alignas(16) static ALfloat HannWindow
[STFT_SIZE
];
94 static void InitHannWindow(void)
98 /* Create lookup table of the Hann window for the desired size, i.e. STFT_SIZE */
99 for(i
= 0;i
< STFT_SIZE
>>1;i
++)
101 ALdouble val
= sin(M_PI
* (ALdouble
)i
/ (ALdouble
)(STFT_SIZE
-1));
102 HannWindow
[i
] = HannWindow
[STFT_SIZE
-(i
+1)] = (ALfloat
)(val
* val
);
105 static alonce_flag HannInitOnce
= AL_ONCE_FLAG_INIT
;
108 /* Converts ALcomplex to ALphasor */
109 static inline ALphasor
rect2polar(ALcomplex number
)
113 polar
.Amplitude
= sqrtf(number
.Real
*number
.Real
+ number
.Imag
*number
.Imag
);
114 polar
.Phase
= atan2f(number
.Imag
, number
.Real
);
119 /* Converts ALphasor to ALcomplex */
120 static inline ALcomplex
polar2rect(ALphasor number
)
124 cartesian
.Real
= number
.Amplitude
* cosf(number
.Phase
);
125 cartesian
.Imag
= number
.Amplitude
* sinf(number
.Phase
);
130 /* Addition of two complex numbers (ALcomplex format) */
131 static inline ALcomplex
complex_add(ALcomplex a
, ALcomplex b
)
135 result
.Real
= a
.Real
+ b
.Real
;
136 result
.Imag
= a
.Imag
+ b
.Imag
;
141 /* Subtraction of two complex numbers (ALcomplex format) */
142 static inline ALcomplex
complex_sub(ALcomplex a
, ALcomplex b
)
146 result
.Real
= a
.Real
- b
.Real
;
147 result
.Imag
= a
.Imag
- b
.Imag
;
152 /* Multiplication of two complex numbers (ALcomplex format) */
153 static inline ALcomplex
complex_mult(ALcomplex a
, ALcomplex b
)
157 result
.Real
= a
.Real
*b
.Real
- a
.Imag
*b
.Imag
;
158 result
.Imag
= a
.Imag
*b
.Real
+ a
.Real
*b
.Imag
;
163 /* Iterative implementation of 2-radix FFT (In-place algorithm). Sign = -1 is
164 * FFT and 1 is iFFT (inverse). Fills FFTBuffer[0...FFTSize-1] with the
165 * Discrete Fourier Transform (DFT) of the time domain data stored in
166 * FFTBuffer[0...FFTSize-1]. FFTBuffer is an array of complex numbers
167 * (ALcomplex), FFTSize MUST BE power of two.
169 static inline ALvoid
FFT(ALcomplex
*FFTBuffer
, ALsizei FFTSize
, ALfloat Sign
)
171 ALsizei i
, j
, k
, mask
, step
, step2
;
172 ALcomplex temp
, u
, w
;
175 /* Bit-reversal permutation applied to a sequence of FFTSize items */
176 for(i
= 1;i
< FFTSize
-1;i
++)
178 for(mask
= 0x1, j
= 0;mask
< FFTSize
;mask
<<= 1)
189 FFTBuffer
[i
] = FFTBuffer
[j
];
194 /* Iterative form of Danielson–Lanczos lemma */
195 for(i
= 1, step
= 2;i
< FFTSize
;i
<<=1, step
<<=1)
201 w
.Imag
= sinf(arg
) * Sign
;
206 for(j
= 0;j
< step2
;j
++)
208 for(k
= j
;k
< FFTSize
;k
+=step
)
210 temp
= complex_mult(FFTBuffer
[k
+step2
], u
);
211 FFTBuffer
[k
+step2
] = complex_sub(FFTBuffer
[k
], temp
);
212 FFTBuffer
[k
] = complex_add(FFTBuffer
[k
], temp
);
215 u
= complex_mult(u
, w
);
221 static void ALpshifterState_Construct(ALpshifterState
*state
)
223 ALeffectState_Construct(STATIC_CAST(ALeffectState
, state
));
224 SET_VTABLE2(ALpshifterState
, ALeffectState
, state
);
226 alcall_once(&HannInitOnce
, InitHannWindow
);
229 static ALvoid
ALpshifterState_Destruct(ALpshifterState
*state
)
231 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
234 static ALboolean
ALpshifterState_deviceUpdate(ALpshifterState
*state
, ALCdevice
*device
)
236 /* (Re-)initializing parameters and clear the buffers. */
237 state
->count
= FIFO_LATENCY
;
238 state
->PitchShift
= 1.0f
;
239 state
->FreqPerBin
= device
->Frequency
/ (ALfloat
)STFT_SIZE
;
241 memset(state
->InFIFO
, 0, sizeof(state
->InFIFO
));
242 memset(state
->OutFIFO
, 0, sizeof(state
->OutFIFO
));
243 memset(state
->FFTbuffer
, 0, sizeof(state
->FFTbuffer
));
244 memset(state
->LastPhase
, 0, sizeof(state
->LastPhase
));
245 memset(state
->SumPhase
, 0, sizeof(state
->SumPhase
));
246 memset(state
->OutputAccum
, 0, sizeof(state
->OutputAccum
));
247 memset(state
->Analysis_buffer
, 0, sizeof(state
->Analysis_buffer
));
248 memset(state
->Syntesis_buffer
, 0, sizeof(state
->Syntesis_buffer
));
250 memset(state
->CurrentGains
, 0, sizeof(state
->CurrentGains
));
251 memset(state
->TargetGains
, 0, sizeof(state
->TargetGains
));
256 static ALvoid
ALpshifterState_update(ALpshifterState
*state
, const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
)
258 const ALCdevice
*device
= context
->Device
;
259 ALfloat coeffs
[MAX_AMBI_COEFFS
];
261 state
->PitchShift
= powf(2.0f
,
262 (ALfloat
)(props
->Pshifter
.CoarseTune
*100 + props
->Pshifter
.FineTune
) / 1200.0f
265 CalcAngleCoeffs(0.0f
, 0.0f
, 0.0f
, coeffs
);
266 ComputeDryPanGains(&device
->Dry
, coeffs
, slot
->Params
.Gain
, state
->TargetGains
);
269 static ALvoid
ALpshifterState_process(ALpshifterState
*state
, ALsizei SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
271 /* Pitch shifter engine based on the work of Stephan Bernsee.
272 * http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
275 static const ALfloat expected
= F_TAU
/ (ALfloat
)OVERSAMP
;
276 const ALfloat freq_per_bin
= state
->FreqPerBin
;
277 ALfloat
*restrict bufferOut
= state
->BufferOut
;
278 ALsizei count
= state
->count
;
281 for(i
= 0;i
< SamplesToDo
;)
284 /* Fill FIFO buffer with samples data */
285 state
->InFIFO
[count
] = SamplesIn
[0][i
];
286 bufferOut
[i
] = state
->OutFIFO
[count
- FIFO_LATENCY
];
289 } while(++i
< SamplesToDo
&& count
< STFT_SIZE
);
291 /* Check whether FIFO buffer is filled */
292 if(count
< STFT_SIZE
) break;
293 count
= FIFO_LATENCY
;
295 /* Real signal windowing and store in FFTbuffer */
296 for(k
= 0;k
< STFT_SIZE
;k
++)
298 state
->FFTbuffer
[k
].Real
= state
->InFIFO
[k
] * HannWindow
[k
];
299 state
->FFTbuffer
[k
].Imag
= 0.0f
;
303 /* Apply FFT to FFTbuffer data */
304 FFT(state
->FFTbuffer
, STFT_SIZE
, -1.0f
);
306 /* Analyze the obtained data. Since the real FFT is symmetric, only
307 * STFT_HALF_SIZE+1 samples are needed.
309 for(k
= 0;k
< STFT_HALF_SIZE
+1;k
++)
315 /* Compute amplitude and phase */
316 component
= rect2polar(state
->FFTbuffer
[k
]);
318 /* Compute phase difference and subtract expected phase difference */
319 tmp
= (component
.Phase
- state
->LastPhase
[k
]) - (ALfloat
)k
*expected
;
321 /* Map delta phase into +/- Pi interval */
322 qpd
= fastf2i(tmp
/ F_PI
);
323 tmp
-= F_PI
* (ALfloat
)(qpd
+ (qpd
%2));
325 /* Get deviation from bin frequency from the +/- Pi interval */
328 /* Compute the k-th partials' true frequency, twice the amplitude
329 * for maintain the gain (because half of bins are used) and store
330 * amplitude and true frequency in analysis buffer.
332 state
->Analysis_buffer
[k
].Amplitude
= 2.0f
* component
.Amplitude
;
333 state
->Analysis_buffer
[k
].Frequency
= ((ALfloat
)k
+ tmp
) * freq_per_bin
;
335 /* Store actual phase[k] for the calculations in the next frame*/
336 state
->LastPhase
[k
] = component
.Phase
;
341 for(k
= 0;k
< STFT_HALF_SIZE
+1;k
++)
343 state
->Syntesis_buffer
[k
].Amplitude
= 0.0f
;
344 state
->Syntesis_buffer
[k
].Frequency
= 0.0f
;
347 for(k
= 0;k
< STFT_HALF_SIZE
+1;k
++)
349 j
= fastf2i((ALfloat
)k
* state
->PitchShift
);
350 if(j
>= STFT_HALF_SIZE
+1) break;
352 state
->Syntesis_buffer
[j
].Amplitude
+= state
->Analysis_buffer
[k
].Amplitude
;
353 state
->Syntesis_buffer
[j
].Frequency
= state
->Analysis_buffer
[k
].Frequency
*
358 /* Synthesis the processing data */
359 for(k
= 0;k
< STFT_HALF_SIZE
+1;k
++)
364 /* Compute bin deviation from scaled freq */
365 tmp
= state
->Syntesis_buffer
[k
].Frequency
/freq_per_bin
- (ALfloat
)k
;
367 /* Calculate actual delta phase and accumulate it to get bin phase */
368 state
->SumPhase
[k
] += ((ALfloat
)k
+ tmp
) * expected
;
370 component
.Amplitude
= state
->Syntesis_buffer
[k
].Amplitude
;
371 component
.Phase
= state
->SumPhase
[k
];
373 /* Compute phasor component to cartesian complex number and storage it into FFTbuffer*/
374 state
->FFTbuffer
[k
] = polar2rect(component
);
376 /* zero negative frequencies for recontruct a real signal */
377 for(k
= STFT_HALF_SIZE
+1;k
< STFT_SIZE
;k
++)
379 state
->FFTbuffer
[k
].Real
= 0.0f
;
380 state
->FFTbuffer
[k
].Imag
= 0.0f
;
383 /* Apply iFFT to buffer data */
384 FFT(state
->FFTbuffer
, STFT_SIZE
, 1.0f
);
386 /* Windowing and add to output */
387 for(k
= 0;k
< STFT_SIZE
;k
++)
388 state
->OutputAccum
[k
] += HannWindow
[k
] * state
->FFTbuffer
[k
].Real
/
389 (0.5f
* STFT_HALF_SIZE
* OVERSAMP
);
391 /* Shift accumulator, input & output FIFO */
392 for(k
= 0;k
< STFT_STEP
;k
++) state
->OutFIFO
[k
] = state
->OutputAccum
[k
];
393 for(j
= 0;k
< STFT_SIZE
;k
++,j
++) state
->OutputAccum
[j
] = state
->OutputAccum
[k
];
394 for(;j
< STFT_SIZE
;j
++) state
->OutputAccum
[j
] = 0.0f
;
395 for(k
= 0;k
< FIFO_LATENCY
;k
++)
396 state
->InFIFO
[k
] = state
->InFIFO
[k
+STFT_STEP
];
398 state
->count
= count
;
400 /* Now, mix the processed sound data to the output. */
401 MixSamples(bufferOut
, NumChannels
, SamplesOut
, state
->CurrentGains
, state
->TargetGains
,
402 maxi(SamplesToDo
, 512), 0, SamplesToDo
);
405 typedef struct PshifterStateFactory
{
406 DERIVE_FROM_TYPE(EffectStateFactory
);
407 } PshifterStateFactory
;
409 static ALeffectState
*PshifterStateFactory_create(PshifterStateFactory
*UNUSED(factory
))
411 ALpshifterState
*state
;
413 NEW_OBJ0(state
, ALpshifterState
)();
414 if(!state
) return NULL
;
416 return STATIC_CAST(ALeffectState
, state
);
419 DEFINE_EFFECTSTATEFACTORY_VTABLE(PshifterStateFactory
);
421 EffectStateFactory
*PshifterStateFactory_getFactory(void)
423 static PshifterStateFactory PshifterFactory
= { { GET_VTABLE2(PshifterStateFactory
, EffectStateFactory
) } };
425 return STATIC_CAST(EffectStateFactory
, &PshifterFactory
);
429 void ALpshifter_setParamf(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALfloat
UNUSED(val
))
431 alSetError( context
, AL_INVALID_ENUM
, "Invalid pitch shifter float property 0x%04x", param
);
434 void ALpshifter_setParamfv(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, const ALfloat
*UNUSED(vals
))
436 alSetError( context
, AL_INVALID_ENUM
, "Invalid pitch shifter float-vector property 0x%04x", param
);
439 void ALpshifter_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
441 ALeffectProps
*props
= &effect
->Props
;
444 case AL_PITCH_SHIFTER_COARSE_TUNE
:
445 if(!(val
>= AL_PITCH_SHIFTER_MIN_COARSE_TUNE
&& val
<= AL_PITCH_SHIFTER_MAX_COARSE_TUNE
))
446 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Pitch shifter coarse tune out of range");
447 props
->Pshifter
.CoarseTune
= val
;
450 case AL_PITCH_SHIFTER_FINE_TUNE
:
451 if(!(val
>= AL_PITCH_SHIFTER_MIN_FINE_TUNE
&& val
<= AL_PITCH_SHIFTER_MAX_FINE_TUNE
))
452 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Pitch shifter fine tune out of range");
453 props
->Pshifter
.FineTune
= val
;
457 alSetError(context
, AL_INVALID_ENUM
, "Invalid pitch shifter integer property 0x%04x", param
);
460 void ALpshifter_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
462 ALpshifter_setParami(effect
, context
, param
, vals
[0]);
465 void ALpshifter_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
467 const ALeffectProps
*props
= &effect
->Props
;
470 case AL_PITCH_SHIFTER_COARSE_TUNE
:
471 *val
= (ALint
)props
->Pshifter
.CoarseTune
;
473 case AL_PITCH_SHIFTER_FINE_TUNE
:
474 *val
= (ALint
)props
->Pshifter
.FineTune
;
478 alSetError(context
, AL_INVALID_ENUM
, "Invalid pitch shifter integer property 0x%04x", param
);
481 void ALpshifter_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
483 ALpshifter_getParami(effect
, context
, param
, vals
);
486 void ALpshifter_getParamf(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALfloat
*UNUSED(val
))
488 alSetError(context
, AL_INVALID_ENUM
, "Invalid pitch shifter float property 0x%04x", param
);
491 void ALpshifter_getParamfv(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALfloat
*UNUSED(vals
))
493 alSetError(context
, AL_INVALID_ENUM
, "Invalid pitch shifter float vector-property 0x%04x", param
);
496 DEFINE_ALEFFECT_VTABLE(ALpshifter
);