react-native-audio-api 0.7.2-nightly-c06331b-20250824 → 0.7.2-nightly-799cc6b-20250826
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +1 -1
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +1 -1
- package/common/cpp/audioapi/libs/signalsmith-stretch/fft-pffft.h +222 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/fft.h +116 -100
- package/common/cpp/audioapi/libs/signalsmith-stretch/signalsmith-stretch.h +299 -41
- package/common/cpp/audioapi/libs/signalsmith-stretch/stft.h +89 -83
- package/package.json +1 -1
- package/common/cpp/audioapi/libs/signalsmith-stretch/fft-accelerate.h +0 -326
|
@@ -14,7 +14,7 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode(
|
|
|
14
14
|
BaseAudioContext *context)
|
|
15
15
|
: AudioBufferBaseSourceNode(context) {
|
|
16
16
|
buffers_ = {};
|
|
17
|
-
stretch_->presetDefault(channelCount_, context_->getSampleRate()
|
|
17
|
+
stretch_->presetDefault(channelCount_, context_->getSampleRate());
|
|
18
18
|
|
|
19
19
|
isInitialized_ = true;
|
|
20
20
|
}
|
|
@@ -93,7 +93,7 @@ void AudioBufferSourceNode::setBuffer(
|
|
|
93
93
|
|
|
94
94
|
loopEnd_ = buffer_->getDuration();
|
|
95
95
|
|
|
96
|
-
stretch_->presetDefault(channelCount_, buffer_->getSampleRate()
|
|
96
|
+
stretch_->presetDefault(channelCount_, buffer_->getSampleRate());
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
void AudioBufferSourceNode::start(double when, double offset, double duration) {
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#ifndef SIGNALSMITH_LINEAR_PLATFORM_FFT_PFFFT_H
|
|
2
|
+
#define SIGNALSMITH_LINEAR_PLATFORM_FFT_PFFFT_H
|
|
3
|
+
|
|
4
|
+
#if defined(__has_include) && !__has_include("pffft/pffft.h")
|
|
5
|
+
# include <audioapi/libs/pffft/pffft.h>
|
|
6
|
+
#else
|
|
7
|
+
# include "pffft/pffft.h"
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
#include <memory>
|
|
11
|
+
#include <cmath>
|
|
12
|
+
#include <complex>
|
|
13
|
+
#include <cassert>
|
|
14
|
+
#include <cstring>
|
|
15
|
+
|
|
16
|
+
namespace signalsmith { namespace linear {
|
|
17
|
+
|
|
18
|
+
template<>
|
|
19
|
+
struct Pow2FFT<float> {
|
|
20
|
+
static constexpr bool prefersSplit = false;
|
|
21
|
+
|
|
22
|
+
using Complex = std::complex<float>;
|
|
23
|
+
|
|
24
|
+
Pow2FFT(size_t size=0) {
|
|
25
|
+
resize(size);
|
|
26
|
+
}
|
|
27
|
+
~Pow2FFT() {
|
|
28
|
+
resize(0); // frees everything
|
|
29
|
+
}
|
|
30
|
+
// Allow move, but not copy
|
|
31
|
+
Pow2FFT(const Pow2FFT &other) = delete;
|
|
32
|
+
Pow2FFT(Pow2FFT &&other) : _size(other._size), hasSetup(other.hasSetup), fftSetup(other.fftSetup), fallback(std::move(other.fallback)), work(other.work), tmpAligned(other.tmpAligned) {
|
|
33
|
+
// Avoid double-free
|
|
34
|
+
other.hasSetup = false;
|
|
35
|
+
other.work = nullptr;
|
|
36
|
+
other.tmpAligned = nullptr;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void resize(size_t size) {
|
|
40
|
+
_size = size;
|
|
41
|
+
fallback = nullptr;
|
|
42
|
+
if (hasSetup) pffft_destroy_setup(fftSetup);
|
|
43
|
+
if (work) pffft_aligned_free(work);
|
|
44
|
+
work = nullptr;
|
|
45
|
+
if (tmpAligned) pffft_aligned_free(tmpAligned);
|
|
46
|
+
tmpAligned = nullptr;
|
|
47
|
+
|
|
48
|
+
// We use this for split-real, even if there's no PFFFT setup
|
|
49
|
+
tmpAligned = (float *)pffft_aligned_malloc(sizeof(float)*size*2);
|
|
50
|
+
|
|
51
|
+
if (size < 16) {
|
|
52
|
+
// PFFFT doesn't support smaller sizes
|
|
53
|
+
hasSetup = false;
|
|
54
|
+
fallback = std::unique_ptr<SimpleFFT<float>>{
|
|
55
|
+
new SimpleFFT<float>(size)
|
|
56
|
+
};
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
work = (float *)pffft_aligned_malloc(sizeof(float)*size*2);
|
|
61
|
+
fftSetup = pffft_new_setup(int(size), PFFFT_COMPLEX);
|
|
62
|
+
hasSetup = fftSetup;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
void fft(const Complex* input, Complex* output) {
|
|
66
|
+
if (fallback) return fallback->fft(input, output);
|
|
67
|
+
fftInner(input, output, PFFFT_FORWARD);
|
|
68
|
+
}
|
|
69
|
+
void fft(const float *inR, const float *inI, float *outR, float *outI) {
|
|
70
|
+
if (fallback) return fallback->fft(inR, inI, outR, outI);
|
|
71
|
+
fftInner(inR, inI, outR, outI, PFFFT_FORWARD);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
void ifft(const Complex* input, Complex* output) {
|
|
75
|
+
if (fallback) return fallback->ifft(input, output);
|
|
76
|
+
fftInner(input, output, PFFFT_BACKWARD);
|
|
77
|
+
}
|
|
78
|
+
void ifft(const float *inR, const float *inI, float *outR, float *outI) {
|
|
79
|
+
if (fallback) return fallback->ifft(inR, inI, outR, outI);
|
|
80
|
+
fftInner(inR, inI, outR, outI, PFFFT_BACKWARD);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private:
|
|
84
|
+
void fftInner(const Complex *input, Complex *output, pffft_direction_t direction) {
|
|
85
|
+
// 16-byte alignment
|
|
86
|
+
if (size_t(input)&0x0F) {
|
|
87
|
+
// `tmpAligned` is always aligned, so copy into that
|
|
88
|
+
std::memcpy(tmpAligned, input, sizeof(Complex)*_size);
|
|
89
|
+
input = (const Complex *)tmpAligned;
|
|
90
|
+
}
|
|
91
|
+
if (size_t(output)&0x0F) {
|
|
92
|
+
// Output to `tmpAligned` - might be in-place if input is unaligned, but that's fine
|
|
93
|
+
pffft_transform_ordered(fftSetup, (const float *)input, tmpAligned, work, direction);
|
|
94
|
+
std::memcpy(output, tmpAligned, sizeof(Complex)*_size);
|
|
95
|
+
} else {
|
|
96
|
+
pffft_transform_ordered(fftSetup, (const float *)input, (float *)output, work, direction);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
void fftInner(const float *inR, const float *inI, float *outR, float *outI, pffft_direction_t direction) {
|
|
100
|
+
for (size_t i = 0; i < _size; ++i) {
|
|
101
|
+
tmpAligned[2*i] = inR[i];
|
|
102
|
+
tmpAligned[2*i + 1] = inI[i];
|
|
103
|
+
}
|
|
104
|
+
// PFFFT supports in-place transforms
|
|
105
|
+
fftInner((const Complex *)tmpAligned, (Complex *)tmpAligned, direction);
|
|
106
|
+
// Un-interleave
|
|
107
|
+
for (size_t i = 0; i < _size; ++i) {
|
|
108
|
+
outR[i] = tmpAligned[2*i];
|
|
109
|
+
outI[i] = tmpAligned[2*i + 1];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
size_t _size = 0;
|
|
114
|
+
bool hasSetup = false;
|
|
115
|
+
PFFFT_Setup *fftSetup = nullptr;
|
|
116
|
+
std::unique_ptr<SimpleFFT<float>> fallback;
|
|
117
|
+
float *work = nullptr, *tmpAligned = nullptr;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
template<>
|
|
121
|
+
struct Pow2RealFFT<float> {
|
|
122
|
+
private:
|
|
123
|
+
using FallbackFFT = SimpleRealFFT<float>; // this wraps the complex one
|
|
124
|
+
public:
|
|
125
|
+
static constexpr bool prefersSplit = false;
|
|
126
|
+
|
|
127
|
+
using Complex = std::complex<float>;
|
|
128
|
+
|
|
129
|
+
Pow2RealFFT(size_t size=0) {
|
|
130
|
+
resize(size);
|
|
131
|
+
}
|
|
132
|
+
~Pow2RealFFT() {
|
|
133
|
+
resize(0);
|
|
134
|
+
}
|
|
135
|
+
// Allow move, but not copy
|
|
136
|
+
Pow2RealFFT(const Pow2RealFFT &other) = delete;
|
|
137
|
+
Pow2RealFFT(Pow2RealFFT &&other) : _size(other._size), hasSetup(other.hasSetup), fftSetup(other.fftSetup), fallback(std::move(other.fallback)), work(other.work), tmpAligned(other.tmpAligned) {
|
|
138
|
+
// Avoid double-free
|
|
139
|
+
other.hasSetup = false;
|
|
140
|
+
other.work = nullptr;
|
|
141
|
+
other.tmpAligned = nullptr;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
void resize(size_t size) {
|
|
145
|
+
_size = size;
|
|
146
|
+
fallback = nullptr;
|
|
147
|
+
if (hasSetup) pffft_destroy_setup(fftSetup);
|
|
148
|
+
if (work) pffft_aligned_free(work);
|
|
149
|
+
work = nullptr;
|
|
150
|
+
if (tmpAligned) pffft_aligned_free(tmpAligned);
|
|
151
|
+
tmpAligned = nullptr;
|
|
152
|
+
|
|
153
|
+
// We use this for split-real, even if there's no PFFFT setup
|
|
154
|
+
tmpAligned = (float *)pffft_aligned_malloc(sizeof(float)*size*2);
|
|
155
|
+
|
|
156
|
+
// TODO: just go for it, and check for success before allocating `work`
|
|
157
|
+
if (size < 32) {
|
|
158
|
+
// PFFFT doesn't support smaller sizes
|
|
159
|
+
hasSetup = false;
|
|
160
|
+
fallback = std::unique_ptr<FallbackFFT>{
|
|
161
|
+
new FallbackFFT(size)
|
|
162
|
+
};
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
work = (float *)pffft_aligned_malloc(sizeof(float)*size);
|
|
167
|
+
fftSetup = pffft_new_setup(int(size), PFFFT_REAL);
|
|
168
|
+
hasSetup = fftSetup;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
void fft(const float *input, Complex *output) {
|
|
172
|
+
if (fallback) return fallback->fft(input, output);
|
|
173
|
+
fftInner(input, (float *)output, PFFFT_FORWARD);
|
|
174
|
+
}
|
|
175
|
+
void fft(const float *inR, float *outR, float *outI) {
|
|
176
|
+
if (fallback) return fallback->fft(inR, outR, outI);
|
|
177
|
+
fftInner(inR, tmpAligned, PFFFT_FORWARD);
|
|
178
|
+
for (size_t i = 0; i < _size/2; ++i) {
|
|
179
|
+
outR[i] = tmpAligned[2*i];
|
|
180
|
+
outI[i] = tmpAligned[2*i + 1];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
void ifft(const Complex *input, float *output) {
|
|
185
|
+
if (fallback) return fallback->ifft(input, output);
|
|
186
|
+
fftInner((const float *)input, output, PFFFT_BACKWARD);
|
|
187
|
+
}
|
|
188
|
+
void ifft(const float *inR, const float *inI, float *outR) {
|
|
189
|
+
if (fallback) return fallback->ifft(inR, inI, outR);
|
|
190
|
+
for (size_t i = 0; i < _size/2; ++i) {
|
|
191
|
+
tmpAligned[2*i] = inR[i];
|
|
192
|
+
tmpAligned[2*i + 1] = inI[i];
|
|
193
|
+
}
|
|
194
|
+
fftInner(tmpAligned, outR, PFFFT_BACKWARD);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private:
|
|
198
|
+
void fftInner(const float *input, float *output, pffft_direction_t direction) {
|
|
199
|
+
// 16-byte alignment
|
|
200
|
+
if (size_t(input)&0x0F) {
|
|
201
|
+
// `tmpAligned` is always aligned, so copy into that
|
|
202
|
+
std::memcpy(tmpAligned, input, sizeof(float)*_size);
|
|
203
|
+
input = tmpAligned;
|
|
204
|
+
}
|
|
205
|
+
if (size_t(output)&0x0F) {
|
|
206
|
+
// Output to `tmpAligned` - might be in-place if input is unaligned, but that's fine
|
|
207
|
+
pffft_transform_ordered(fftSetup, input, tmpAligned, work, direction);
|
|
208
|
+
std::memcpy(output, tmpAligned, sizeof(float)*_size);
|
|
209
|
+
} else {
|
|
210
|
+
pffft_transform_ordered(fftSetup, input, output, work, direction);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
size_t _size = 0;
|
|
215
|
+
bool hasSetup = false;
|
|
216
|
+
PFFFT_Setup *fftSetup = nullptr;
|
|
217
|
+
std::unique_ptr<FallbackFFT> fallback;
|
|
218
|
+
float *work = nullptr, *tmpAligned = nullptr;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
}} // namespace
|
|
222
|
+
#endif // include guard
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include <complex>
|
|
5
5
|
#include <vector>
|
|
6
6
|
#include <cmath>
|
|
7
|
+
#include <cstring>
|
|
7
8
|
|
|
8
9
|
#if defined(__FAST_MATH__) && (__apple_build_version__ >= 16000000) && (__apple_build_version__ <= 16000099) && !defined(SIGNALSMITH_IGNORE_BROKEN_APPLECLANG)
|
|
9
10
|
# error Apple Clang 16.0.0 generates incorrect SIMD for ARM. If you HAVE to use this version of Clang, turn off -ffast-math.
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
namespace signalsmith { namespace linear {
|
|
17
18
|
|
|
18
19
|
namespace _impl {
|
|
20
|
+
// Helpers for complex arithmetic, ignoring the NaN/Inf edge-cases you get without `-ffast-math`
|
|
19
21
|
template<class V>
|
|
20
22
|
void complexMul(std::complex<V> *a, const std::complex<V> *b, const std::complex<V> *c, size_t size) {
|
|
21
23
|
for (size_t i = 0; i < size; ++i) {
|
|
@@ -282,79 +284,6 @@ private:
|
|
|
282
284
|
}
|
|
283
285
|
};
|
|
284
286
|
|
|
285
|
-
// Wraps a complex FFT into a real one
|
|
286
|
-
template<typename Sample, class ComplexFFT=SimpleFFT<Sample>>
|
|
287
|
-
struct SimpleRealFFT {
|
|
288
|
-
using Complex = std::complex<Sample>;
|
|
289
|
-
|
|
290
|
-
SimpleRealFFT(size_t size=0) {
|
|
291
|
-
resize(size);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
void resize(size_t size) {
|
|
295
|
-
complexFft.resize(size);
|
|
296
|
-
tmpTime.resize(size);
|
|
297
|
-
tmpFreq.resize(size);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
void fft(const Sample *time, Complex *freq) {
|
|
301
|
-
for (size_t i = 0; i < tmpTime.size(); ++i) {
|
|
302
|
-
tmpTime[i] = time[i];
|
|
303
|
-
}
|
|
304
|
-
complexFft.fft(tmpTime.data(), tmpFreq.data());
|
|
305
|
-
for (size_t i = 0; i < tmpFreq.size()/2; ++i) {
|
|
306
|
-
freq[i] = tmpFreq[i];
|
|
307
|
-
}
|
|
308
|
-
freq[0] = {
|
|
309
|
-
tmpFreq[0].real(),
|
|
310
|
-
tmpFreq[tmpFreq.size()/2].real()
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
void fft(const Sample *inR, Sample *outR, Sample *outI) {
|
|
314
|
-
Sample *tmpFreqR = (Sample *)tmpFreq.data(), *tmpFreqI = tmpFreqR + tmpFreq.size();
|
|
315
|
-
for (size_t i = 0; i < tmpTime.size()/2; ++i) {
|
|
316
|
-
tmpTime[i] = 0;
|
|
317
|
-
}
|
|
318
|
-
complexFft.fft(inR, (const Sample *)tmpTime.data(), tmpFreqR, tmpFreqI);
|
|
319
|
-
for (size_t i = 0; i < tmpTime.size()/2; ++i) {
|
|
320
|
-
outR[i] = tmpFreqR[i];
|
|
321
|
-
outI[i] = tmpFreqI[i];
|
|
322
|
-
}
|
|
323
|
-
outI[0] = tmpFreqR[tmpFreq.size()/2];
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
void ifft(const Complex *freq, Sample *time) {
|
|
327
|
-
tmpFreq[0] = freq[0].real();
|
|
328
|
-
tmpFreq[tmpFreq.size()/2] = freq[0].imag();
|
|
329
|
-
for (size_t i = 1; i < tmpFreq.size()/2; ++i) {
|
|
330
|
-
tmpFreq[i] = freq[i];
|
|
331
|
-
tmpFreq[tmpFreq.size() - i] = std::conj(freq[i]);
|
|
332
|
-
}
|
|
333
|
-
complexFft.ifft(tmpFreq.data(), tmpTime.data());
|
|
334
|
-
for (size_t i = 0; i < tmpTime.size(); ++i) {
|
|
335
|
-
time[i] = tmpTime[i].real();
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
void ifft(const Sample *inR, const Sample *inI, Sample *outR) {
|
|
339
|
-
Sample *tmpFreqR = (Sample *)tmpFreq.data(), *tmpFreqI = tmpFreqR + tmpFreq.size();
|
|
340
|
-
tmpFreqR[0] = inR[0];
|
|
341
|
-
tmpFreqR[tmpFreq.size()/2] = inI[0];
|
|
342
|
-
tmpFreqI[0] = 0;
|
|
343
|
-
tmpFreqI[tmpFreq.size()/2] = 0;
|
|
344
|
-
for (size_t i = 1; i < tmpFreq.size()/2; ++i) {
|
|
345
|
-
tmpFreqR[i] = inR[i];
|
|
346
|
-
tmpFreqI[i] = inI[i];
|
|
347
|
-
tmpFreqR[tmpFreq.size() - i] = inR[i];
|
|
348
|
-
tmpFreqI[tmpFreq.size() - i] = -inI[i];
|
|
349
|
-
}
|
|
350
|
-
complexFft.ifft(tmpFreqR, tmpFreqI, outR, (Sample *)tmpTime.data());
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private:
|
|
354
|
-
ComplexFFT complexFft;
|
|
355
|
-
std::vector<Complex> tmpTime, tmpFreq;
|
|
356
|
-
};
|
|
357
|
-
|
|
358
287
|
/// A power-of-2 only FFT, specialised with platform-specific fast implementations where available
|
|
359
288
|
template<typename Sample>
|
|
360
289
|
struct Pow2FFT {
|
|
@@ -364,6 +293,9 @@ struct Pow2FFT {
|
|
|
364
293
|
Pow2FFT(size_t size=0) {
|
|
365
294
|
resize(size);
|
|
366
295
|
}
|
|
296
|
+
// Allow move, but not copy
|
|
297
|
+
Pow2FFT(const Pow2FFT &other) = delete;
|
|
298
|
+
Pow2FFT(Pow2FFT &&other) : tmp(std::move(other.tmp)), simpleFFT(std::move(other.simpleFFT)) {}
|
|
367
299
|
|
|
368
300
|
void resize(size_t size) {
|
|
369
301
|
simpleFFT.resize(size);
|
|
@@ -389,14 +321,6 @@ private:
|
|
|
389
321
|
SimpleFFT<Sample> simpleFFT;
|
|
390
322
|
};
|
|
391
323
|
|
|
392
|
-
/// A power-of-2 only Real FFT, specialised with platform-specific fast implementations where available
|
|
393
|
-
template<typename Sample>
|
|
394
|
-
struct Pow2RealFFT : public SimpleRealFFT<Sample, Pow2FFT<Sample>> {
|
|
395
|
-
static constexpr bool prefersSplit = Pow2FFT<Sample>::prefersSplit;
|
|
396
|
-
|
|
397
|
-
using SimpleRealFFT<Sample, Pow2FFT<Sample>>::SimpleRealFFT;
|
|
398
|
-
};
|
|
399
|
-
|
|
400
324
|
/// An FFT which can handle multiples of 3 and 5, and can be computed in chunks
|
|
401
325
|
template<typename Sample, bool splitComputation=false>
|
|
402
326
|
struct SplitFFT {
|
|
@@ -450,7 +374,6 @@ struct SplitFFT {
|
|
|
450
374
|
outerTwiddlesI[i] = outerTwiddles[i].imag();
|
|
451
375
|
}
|
|
452
376
|
|
|
453
|
-
|
|
454
377
|
StepType interleaveStep = StepType::interleaveOrderN;
|
|
455
378
|
StepType finalStep = StepType::finalOrderN;
|
|
456
379
|
if (outerSize == 2) {
|
|
@@ -942,6 +865,94 @@ private:
|
|
|
942
865
|
template<typename Sample, bool splitComputation=false>
|
|
943
866
|
using FFT = SplitFFT<Sample, splitComputation>;
|
|
944
867
|
|
|
868
|
+
// Wraps a complex FFT into a real one
|
|
869
|
+
template<typename Sample, class ComplexFFT=Pow2FFT<Sample>>
|
|
870
|
+
struct SimpleRealFFT {
|
|
871
|
+
using Complex = std::complex<Sample>;
|
|
872
|
+
|
|
873
|
+
static constexpr bool prefersSplit = ComplexFFT::prefersSplit;
|
|
874
|
+
|
|
875
|
+
SimpleRealFFT(size_t size=0) {
|
|
876
|
+
resize(size);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
void resize(size_t size) {
|
|
880
|
+
complexFft.resize(size);
|
|
881
|
+
tmpTime.resize(size);
|
|
882
|
+
tmpFreq.resize(size);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
void fft(const Sample *time, Complex *freq) {
|
|
886
|
+
for (size_t i = 0; i < tmpTime.size(); ++i) {
|
|
887
|
+
tmpTime[i] = time[i];
|
|
888
|
+
}
|
|
889
|
+
complexFft.fft(tmpTime.data(), tmpFreq.data());
|
|
890
|
+
for (size_t i = 0; i < tmpFreq.size()/2; ++i) {
|
|
891
|
+
freq[i] = tmpFreq[i];
|
|
892
|
+
}
|
|
893
|
+
freq[0] = {
|
|
894
|
+
tmpFreq[0].real(),
|
|
895
|
+
tmpFreq[tmpFreq.size()/2].real()
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
void fft(const Sample *inR, Sample *outR, Sample *outI) {
|
|
899
|
+
Sample *tmpFreqR = (Sample *)tmpFreq.data(), *tmpFreqI = tmpFreqR + tmpFreq.size();
|
|
900
|
+
for (size_t i = 0; i < tmpTime.size()/2; ++i) {
|
|
901
|
+
tmpTime[i] = 0;
|
|
902
|
+
}
|
|
903
|
+
complexFft.fft(inR, (const Sample *)tmpTime.data(), tmpFreqR, tmpFreqI);
|
|
904
|
+
for (size_t i = 0; i < tmpTime.size()/2; ++i) {
|
|
905
|
+
outR[i] = tmpFreqR[i];
|
|
906
|
+
outI[i] = tmpFreqI[i];
|
|
907
|
+
}
|
|
908
|
+
outI[0] = tmpFreqR[tmpFreq.size()/2];
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
void ifft(const Complex *freq, Sample *time) {
|
|
912
|
+
tmpFreq[0] = freq[0].real();
|
|
913
|
+
tmpFreq[tmpFreq.size()/2] = freq[0].imag();
|
|
914
|
+
for (size_t i = 1; i < tmpFreq.size()/2; ++i) {
|
|
915
|
+
tmpFreq[i] = freq[i];
|
|
916
|
+
tmpFreq[tmpFreq.size() - i] = std::conj(freq[i]);
|
|
917
|
+
}
|
|
918
|
+
complexFft.ifft(tmpFreq.data(), tmpTime.data());
|
|
919
|
+
for (size_t i = 0; i < tmpTime.size(); ++i) {
|
|
920
|
+
time[i] = tmpTime[i].real();
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
void ifft(const Sample *inR, const Sample *inI, Sample *outR) {
|
|
924
|
+
Sample *tmpFreqR = (Sample *)tmpFreq.data(), *tmpFreqI = tmpFreqR + tmpFreq.size();
|
|
925
|
+
tmpFreqR[0] = inR[0];
|
|
926
|
+
tmpFreqR[tmpFreq.size()/2] = inI[0];
|
|
927
|
+
tmpFreqI[0] = 0;
|
|
928
|
+
tmpFreqI[tmpFreq.size()/2] = 0;
|
|
929
|
+
for (size_t i = 1; i < tmpFreq.size()/2; ++i) {
|
|
930
|
+
tmpFreqR[i] = inR[i];
|
|
931
|
+
tmpFreqI[i] = inI[i];
|
|
932
|
+
tmpFreqR[tmpFreq.size() - i] = inR[i];
|
|
933
|
+
tmpFreqI[tmpFreq.size() - i] = -inI[i];
|
|
934
|
+
}
|
|
935
|
+
complexFft.ifft(tmpFreqR, tmpFreqI, outR, (Sample *)tmpTime.data());
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
private:
|
|
939
|
+
ComplexFFT complexFft;
|
|
940
|
+
std::vector<Complex> tmpTime, tmpFreq;
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
/// A default power-of-2 FFT, specialised with platform-specific fast implementations where available
|
|
944
|
+
template<typename Sample>
|
|
945
|
+
struct Pow2RealFFT : public SimpleRealFFT<Sample> {
|
|
946
|
+
static constexpr bool prefersSplit = SimpleRealFFT<Sample>::prefersSplit;
|
|
947
|
+
|
|
948
|
+
using SimpleRealFFT<Sample>::SimpleRealFFT;
|
|
949
|
+
|
|
950
|
+
// Prevent copying, since it might be a problem for specialisations
|
|
951
|
+
Pow2RealFFT(const Pow2RealFFT &other) = delete;
|
|
952
|
+
// Pass move-constructor through, just to be explicit about it
|
|
953
|
+
Pow2RealFFT(Pow2RealFFT &&other) : SimpleRealFFT<Sample>(std::move(other)) {}
|
|
954
|
+
};
|
|
955
|
+
|
|
945
956
|
/// A Real FFT which can handle multiples of 3 and 5, and can be computed in chunks
|
|
946
957
|
template<typename Sample, bool splitComputation=false, bool halfBinShift=false>
|
|
947
958
|
struct RealFFT {
|
|
@@ -1048,6 +1059,7 @@ struct RealFFT {
|
|
|
1048
1059
|
}
|
|
1049
1060
|
}
|
|
1050
1061
|
} else {
|
|
1062
|
+
bool canUseTime = !halfBinShift && !(size_t(time)%alignof(Complex));
|
|
1051
1063
|
if (step-- == 0) {
|
|
1052
1064
|
size_t hSize = complexFft.size();
|
|
1053
1065
|
if (halfBinShift) {
|
|
@@ -1059,13 +1071,11 @@ struct RealFFT {
|
|
|
1059
1071
|
ti*twist.real() + tr*twist.imag()
|
|
1060
1072
|
};
|
|
1061
1073
|
}
|
|
1062
|
-
} else {
|
|
1063
|
-
|
|
1064
|
-
tmpTime[i] = {time[2*i], time[2*i + 1]};
|
|
1065
|
-
}
|
|
1074
|
+
} else if (!canUseTime) {
|
|
1075
|
+
std::memcpy(tmpTime.data(), time, sizeof(Complex)*hSize);
|
|
1066
1076
|
}
|
|
1067
1077
|
} else if (step < complexFft.steps()) {
|
|
1068
|
-
complexFft.fft(step, tmpTime.data(), tmpFreq.data());
|
|
1078
|
+
complexFft.fft(step, canUseTime ? (const Complex *)time : tmpTime.data(), tmpFreq.data());
|
|
1069
1079
|
} else {
|
|
1070
1080
|
if (!halfBinShift) {
|
|
1071
1081
|
Complex bin0 = tmpFreq[0];
|
|
@@ -1225,6 +1235,7 @@ struct RealFFT {
|
|
|
1225
1235
|
}
|
|
1226
1236
|
}
|
|
1227
1237
|
} else {
|
|
1238
|
+
bool canUseTime = !halfBinShift && !(size_t(time)%alignof(Complex));
|
|
1228
1239
|
bool splitFirst = splitComputation && (step-- == 0);
|
|
1229
1240
|
if (splitFirst || step-- == 0) {
|
|
1230
1241
|
Complex bin0 = freq[0];
|
|
@@ -1260,7 +1271,7 @@ struct RealFFT {
|
|
|
1260
1271
|
}
|
|
1261
1272
|
} else if (step < complexFft.steps()) {
|
|
1262
1273
|
// Can't just use time as (Complex *), since it might not be aligned properly
|
|
1263
|
-
complexFft.ifft(step, tmpFreq.data(), tmpTime.data());
|
|
1274
|
+
complexFft.ifft(step, tmpFreq.data(), canUseTime ? (Complex *)time : tmpTime.data());
|
|
1264
1275
|
} else {
|
|
1265
1276
|
size_t hSize = complexFft.size();
|
|
1266
1277
|
if (halfBinShift) {
|
|
@@ -1270,11 +1281,8 @@ struct RealFFT {
|
|
|
1270
1281
|
time[2*i] = t.real()*twist.real() + t.imag()*twist.imag();
|
|
1271
1282
|
time[2*i + 1] = t.imag()*twist.real() - t.real()*twist.imag();
|
|
1272
1283
|
}
|
|
1273
|
-
} else {
|
|
1274
|
-
|
|
1275
|
-
time[2*i] = tmpTime[i].real();
|
|
1276
|
-
time[2*i + 1] = tmpTime[i].imag();
|
|
1277
|
-
}
|
|
1284
|
+
} else if (!canUseTime) {
|
|
1285
|
+
std::memcpy(time, tmpTime.data(), sizeof(Complex)*hSize);
|
|
1278
1286
|
}
|
|
1279
1287
|
}
|
|
1280
1288
|
}
|
|
@@ -1343,12 +1351,12 @@ struct RealFFT {
|
|
|
1343
1351
|
}
|
|
1344
1352
|
}
|
|
1345
1353
|
private:
|
|
1346
|
-
static constexpr bool complexPrefersSplit = SplitFFT<Sample, splitComputation>::prefersSplit;
|
|
1347
|
-
std::vector<Complex> tmpFreq, tmpTime;
|
|
1348
|
-
std::vector<Complex> twiddles, halfBinTwists;
|
|
1349
|
-
|
|
1350
1354
|
using ComplexFFT = SplitFFT<Sample, splitComputation>;
|
|
1351
1355
|
ComplexFFT complexFft;
|
|
1356
|
+
|
|
1357
|
+
static constexpr bool complexPrefersSplit = ComplexFFT::prefersSplit;
|
|
1358
|
+
std::vector<Complex> tmpFreq, tmpTime;
|
|
1359
|
+
std::vector<Complex> twiddles, halfBinTwists;
|
|
1352
1360
|
};
|
|
1353
1361
|
|
|
1354
1362
|
template<typename Sample, bool splitComputation=false>
|
|
@@ -1356,9 +1364,17 @@ using ModifiedRealFFT = RealFFT<Sample, splitComputation, true>;
|
|
|
1356
1364
|
|
|
1357
1365
|
}} // namespace
|
|
1358
1366
|
|
|
1359
|
-
//
|
|
1360
|
-
#
|
|
1361
|
-
#
|
|
1367
|
+
// Override `Pow2FFT` / `Pow2RealFFT` templates with faster implementations
|
|
1368
|
+
#include <audioapi/libs/signalsmith-stretch/fft-pffft.h>
|
|
1369
|
+
#if defined(SIGNALSMITH_USE_PFFFT) || defined(SIGNALSMITH_USE_PFFFT_DOUBLE)
|
|
1370
|
+
# if defined(SIGNALSMITH_USE_PFFFT)
|
|
1371
|
+
# include "./platform/fft-pffft.h"
|
|
1372
|
+
# endif
|
|
1373
|
+
# if defined(SIGNALSMITH_USE_PFFFT_DOUBLE)
|
|
1374
|
+
# include "./platform/fft-pffft-double.h"
|
|
1375
|
+
# endif
|
|
1376
|
+
#elif defined(SIGNALSMITH_USE_ACCELERATE)
|
|
1377
|
+
# include "./platform/fft-accelerate.h"
|
|
1362
1378
|
#elif defined(SIGNALSMITH_USE_IPP)
|
|
1363
1379
|
# include "./platform/fft-ipp.h"
|
|
1364
1380
|
#endif
|