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.
@@ -14,7 +14,7 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode(
14
14
  BaseAudioContext *context)
15
15
  : AudioBufferBaseSourceNode(context) {
16
16
  buffers_ = {};
17
- stretch_->presetDefault(channelCount_, context_->getSampleRate(), true);
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(), true);
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
- for (size_t i = 0; i < hSize; ++i) {
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
- for (size_t i = 0; i < hSize; ++i) {
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
- // Platform-specific
1360
- #if defined(HAVE_ACCELERATE)
1361
- # include <audioapi/libs/signalsmith-stretch/fft-accelerate.h>
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