react-native-audio-api 0.7.2-nightly-c06331b-20250824 → 0.7.2-nightly-799cc6b-20250825

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.
@@ -5,9 +5,17 @@
5
5
 
6
6
  namespace signalsmith { namespace linear {
7
7
 
8
+ enum {
9
+ STFT_SPECTRUM_PACKED=0,
10
+ STFT_SPECTRUM_MODIFIED=1,
11
+ STFT_SPECTRUM_UNPACKED=2,
12
+ };
13
+
8
14
  /// A self-normalising STFT, with variable position/window for output blocks
9
- template<typename Sample, bool splitComputation=false, bool modified=false>
15
+ template<typename Sample, bool splitComputation=false, int spectrumType=STFT_SPECTRUM_PACKED>
10
16
  struct DynamicSTFT {
17
+ static constexpr bool modified = (spectrumType == STFT_SPECTRUM_MODIFIED);
18
+ static constexpr bool unpacked = (spectrumType == STFT_SPECTRUM_UNPACKED);
11
19
  RealFFT<Sample, splitComputation, modified> fft;
12
20
 
13
21
  using Complex = std::complex<Sample>;
@@ -16,13 +24,13 @@ struct DynamicSTFT {
16
24
  static constexpr WindowShape acg = WindowShape::acg;
17
25
  static constexpr WindowShape kaiser = WindowShape::kaiser;
18
26
 
19
- void configure(size_t inChannels, size_t outChannels, size_t blockSamples, size_t extraInputHistory=0, size_t intervalSamples=0) {
27
+ void configure(size_t inChannels, size_t outChannels, size_t blockSamples, size_t extraInputHistory=0, size_t intervalSamples=0, Sample asymmetry=0) {
20
28
  _analysisChannels = inChannels;
21
29
  _synthesisChannels = outChannels;
22
30
  _blockSamples = blockSamples;
23
31
  _fftSamples = fft.fastSizeAbove((blockSamples + 1)/2)*2;
24
32
  fft.resize(_fftSamples);
25
- _fftBins = _fftSamples/2;
33
+ _fftBins = _fftSamples/2 + (spectrumType == STFT_SPECTRUM_UNPACKED);
26
34
 
27
35
  _inputLengthSamples = _blockSamples + extraInputHistory;
28
36
  input.buffer.resize(_inputLengthSamples*_analysisChannels);
@@ -34,7 +42,7 @@ struct DynamicSTFT {
34
42
 
35
43
  _analysisWindow.resize(_blockSamples);
36
44
  _synthesisWindow.resize(_blockSamples);
37
- setInterval(intervalSamples ? intervalSamples : blockSamples/4, acg);
45
+ setInterval(intervalSamples ? intervalSamples : blockSamples/4, acg, asymmetry);
38
46
 
39
47
  reset();
40
48
  }
@@ -82,7 +90,7 @@ struct DynamicSTFT {
82
90
  moveOutput(_defaultInterval); // ready for first block immediately
83
91
  }
84
92
 
85
- void writeInput(size_t channel, size_t offset, size_t length, Sample *inputArray) {
93
+ void writeInput(size_t channel, size_t offset, size_t length, const Sample *inputArray) {
86
94
  Sample *buffer = input.buffer.data() + channel*_inputLengthSamples;
87
95
 
88
96
  size_t offsetPos = (input.pos + offset)%_inputLengthSamples;
@@ -97,7 +105,7 @@ struct DynamicSTFT {
97
105
  buffer[i2] = inputArray[i];
98
106
  }
99
107
  }
100
- void writeInput(size_t channel, size_t length, Sample *inputArray) {
108
+ void writeInput(size_t channel, size_t length, const Sample *inputArray) {
101
109
  writeInput(channel, 0, length, inputArray);
102
110
  }
103
111
  void moveInput(size_t samples, bool clearInput=false) {
@@ -161,6 +169,42 @@ struct DynamicSTFT {
161
169
  void readOutput(size_t channel, size_t length, Sample *outputArray) {
162
170
  return readOutput(channel, 0, length, outputArray);
163
171
  }
172
+ void addOutput(size_t channel, size_t offset, size_t length, const Sample *newOutputArray) {
173
+ length = std::min(_blockSamples, length);
174
+ Sample *buffer = output.buffer.data() + channel*_blockSamples;
175
+ size_t offsetPos = (output.pos + offset)%_blockSamples;
176
+ size_t outputWrapIndex = _blockSamples - offsetPos;
177
+ size_t chunk1 = std::min(length, outputWrapIndex);
178
+ for (size_t i = 0; i < chunk1; ++i) {
179
+ size_t i2 = offsetPos + i;
180
+ buffer[i2] += newOutputArray[i]*output.windowProducts[i2];
181
+ }
182
+ for (size_t i = chunk1; i < length; ++i) {
183
+ size_t i2 = i + offsetPos - _blockSamples;
184
+ buffer[i2] += newOutputArray[i]*output.windowProducts[i2];
185
+ }
186
+ }
187
+ void addOutput(size_t channel, size_t length, const Sample *newOutputArray) {
188
+ return addOutput(channel, 0, length, newOutputArray);
189
+ }
190
+ void replaceOutput(size_t channel, size_t offset, size_t length, const Sample *newOutputArray) {
191
+ length = std::min(_blockSamples, length);
192
+ Sample *buffer = output.buffer.data() + channel*_blockSamples;
193
+ size_t offsetPos = (output.pos + offset)%_blockSamples;
194
+ size_t outputWrapIndex = _blockSamples - offsetPos;
195
+ size_t chunk1 = std::min(length, outputWrapIndex);
196
+ for (size_t i = 0; i < chunk1; ++i) {
197
+ size_t i2 = offsetPos + i;
198
+ buffer[i2] = newOutputArray[i]*output.windowProducts[i2];
199
+ }
200
+ for (size_t i = chunk1; i < length; ++i) {
201
+ size_t i2 = i + offsetPos - _blockSamples;
202
+ buffer[i2] = newOutputArray[i]*output.windowProducts[i2];
203
+ }
204
+ }
205
+ void replaceOutput(size_t channel, size_t length, const Sample *newOutputArray) {
206
+ return replaceOutput(channel, 0, length, newOutputArray);
207
+ }
164
208
  void moveOutput(size_t samples) {
165
209
  if (samples == 1) { // avoid all the loops/chunks if we can
166
210
  for (size_t c = 0; c < _synthesisChannels; ++c) {
@@ -233,27 +277,35 @@ struct DynamicSTFT {
233
277
  return _synthesisOffset;
234
278
  }
235
279
 
236
- void setInterval(size_t defaultInterval, WindowShape windowShape=WindowShape::ignore) {
280
+ void setInterval(size_t defaultInterval, WindowShape windowShape=WindowShape::ignore, Sample asymmetry=0) {
237
281
  _defaultInterval = defaultInterval;
238
282
  if (windowShape == WindowShape::ignore) return;
239
283
 
240
- _analysisOffset = _synthesisOffset = _blockSamples/2;
241
-
242
284
  if (windowShape == acg) {
243
285
  auto window = ApproximateConfinedGaussian::withBandwidth(double(_blockSamples)/defaultInterval);
244
- window.fill(_synthesisWindow, _blockSamples);
286
+ window.fill(_synthesisWindow, _blockSamples, asymmetry, false);
245
287
  } else if (windowShape == kaiser) {
246
288
  auto window = Kaiser::withBandwidth(double(_blockSamples)/defaultInterval, true);
247
- window.fill(_synthesisWindow, _blockSamples);
289
+ window.fill(_synthesisWindow, _blockSamples, asymmetry, true);
248
290
  }
249
291
 
292
+ _analysisOffset = _synthesisOffset = _blockSamples/2;
250
293
  if (_analysisChannels == 0) {
251
294
  for (auto &v : _analysisWindow) v = 1;
252
- } else {
295
+ } else if (asymmetry == 0) {
253
296
  forcePerfectReconstruction(_synthesisWindow, _blockSamples, _defaultInterval);
254
297
  for (size_t i = 0; i < _blockSamples; ++i) {
255
298
  _analysisWindow[i] = _synthesisWindow[i];
256
299
  }
300
+ } else {
301
+ for (size_t i = 0; i < _blockSamples; ++i) {
302
+ _analysisWindow[i] = _synthesisWindow[_blockSamples - 1 - i];
303
+ }
304
+ }
305
+ // Set offsets to peak's index
306
+ for (size_t i = 0; i < _blockSamples; ++i) {
307
+ if (_analysisWindow[i] > _analysisWindow[_analysisOffset]) _analysisOffset = i;
308
+ if (_synthesisWindow[i] > _synthesisWindow[_synthesisOffset]) _synthesisOffset = i;
257
309
  }
258
310
  }
259
311
 
@@ -307,10 +359,19 @@ struct DynamicSTFT {
307
359
  }
308
360
  if (splitComputation) return;
309
361
  }
362
+ auto *spectrumPtr = spectrum(channel);
310
363
  if (splitComputation) {
311
- fft.fft(step, timeBuffer.data(), spectrum(channel));
364
+ fft.fft(step, timeBuffer.data(), spectrumPtr);
365
+ if (unpacked && step == fft.steps() - 1) {
366
+ spectrumPtr[_fftBins - 1] = spectrumPtr[0].imag();
367
+ spectrumPtr[0].imag(0);
368
+ }
312
369
  } else {
313
370
  fft.fft(timeBuffer.data(), spectrum(channel));
371
+ if (unpacked) {
372
+ spectrumPtr[_fftBins - 1] = spectrumPtr[0].imag();
373
+ spectrumPtr[0].imag(0);
374
+ }
314
375
  }
315
376
  }
316
377
 
@@ -333,13 +394,18 @@ struct DynamicSTFT {
333
394
  size_t channel = step/(fftSteps + 1);
334
395
  step -= channel*(fftSteps + 1);
335
396
 
397
+ auto *spectrumPtr = spectrum(channel);
398
+ if (unpacked && step == 0) { // re-pack
399
+ spectrumPtr[0].imag(spectrumPtr[_fftBins - 1].real());
400
+ }
401
+
336
402
  if (splitComputation) {
337
403
  if (step < fftSteps) {
338
- fft.ifft(step, spectrum(channel), timeBuffer.data());
404
+ fft.ifft(step, spectrumPtr, timeBuffer.data());
339
405
  return;
340
406
  }
341
407
  } else {
342
- fft.ifft(spectrum(channel), timeBuffer.data());
408
+ fft.ifft(spectrumPtr, timeBuffer.data());
343
409
  }
344
410
 
345
411
  // extra step after each channel's FFT
@@ -501,75 +567,13 @@ private:
501
567
  return alpha*M_PI;
502
568
  }
503
569
 
504
- static double betaToBandwidth(double beta) {
505
- double alpha = beta*(1.0/M_PI);
506
- return 2*std::sqrt(alpha*alpha + 1);
507
- }
508
- static double bandwidthToEnergyDb(double bandwidth, bool heuristicOptimal=false) {
509
- // Horrible heuristic fits
510
- if (heuristicOptimal) {
511
- if (bandwidth < 3) bandwidth += (3 - bandwidth)*0.5;
512
- return 12.9 + -3/(bandwidth + 0.4) - 13.4*bandwidth + (bandwidth < 3)*-9.6*(bandwidth - 3);
513
- }
514
- return 10.5 + 15/(bandwidth + 0.4) - 13.25*bandwidth + (bandwidth < 2)*13*(bandwidth - 2);
515
- }
516
- static double energyDbToBandwidth(double energyDb, bool heuristicOptimal=false) {
517
- double bw = 1;
518
- while (bw < 20 && bandwidthToEnergyDb(bw, heuristicOptimal) > energyDb) {
519
- bw *= 2;
520
- }
521
- double step = bw/2;
522
- while (step > 0.0001) {
523
- if (bandwidthToEnergyDb(bw, heuristicOptimal) > energyDb) {
524
- bw += step;
525
- } else {
526
- bw -= step;
527
- }
528
- step *= 0.5;
529
- }
530
- return bw;
531
- }
532
- static double bandwidthToPeakDb(double bandwidth, bool heuristicOptimal=false) {
533
- // Horrible heuristic fits
534
- if (heuristicOptimal) {
535
- return 14.2 - 20/(bandwidth + 1) - 13*bandwidth + (bandwidth < 3)*-6*(bandwidth - 3) + (bandwidth < 2.25)*5.8*(bandwidth - 2.25);
536
- }
537
- return 10 + 8/(bandwidth + 2) - 12.75*bandwidth + (bandwidth < 2)*4*(bandwidth - 2);
538
- }
539
- static double peakDbToBandwidth(double peakDb, bool heuristicOptimal=false) {
540
- double bw = 1;
541
- while (bw < 20 && bandwidthToPeakDb(bw, heuristicOptimal) > peakDb) {
542
- bw *= 2;
543
- }
544
- double step = bw/2;
545
- while (step > 0.0001) {
546
- if (bandwidthToPeakDb(bw, heuristicOptimal) > peakDb) {
547
- bw += step;
548
- } else {
549
- bw -= step;
550
- }
551
- step *= 0.5;
552
- }
553
- return bw;
554
- }
555
-
556
- static double bandwidthToEnbw(double bandwidth, bool heuristicOptimal=false) {
557
- if (heuristicOptimal) bandwidth = heuristicBandwidth(bandwidth);
558
- double b2 = std::max<double>(bandwidth - 2, 0);
559
- return 1 + b2*(0.2 + b2*(-0.005 + b2*(-0.000005 + b2*0.0000022)));
560
- }
561
-
562
- double operator ()(double unit) {
563
- double r = 2*unit - 1;
564
- double arg = std::sqrt(1 - r*r);
565
- return bessel0(beta*arg)*invB0;
566
- }
567
-
568
570
  template<typename Data>
569
- void fill(Data &&data, size_t size) const {
571
+ void fill(Data &&data, size_t size, double warp, bool isForSynthesis) const {
570
572
  double invSize = 1.0/size;
573
+ size_t offsetI = (size&1) ? 1 : (isForSynthesis ? 0 : 2);
571
574
  for (size_t i = 0; i < size; ++i) {
572
- double r = (2*i + 1)*invSize - 1;
575
+ double r = (2*i + offsetI)*invSize - 1;
576
+ r = (r + warp)/(1 + r*warp);
573
577
  double arg = std::sqrt(1 - r*r);
574
578
  data[i] = bessel0(beta*arg)*invB0;
575
579
  }
@@ -594,12 +598,14 @@ private:
594
598
 
595
599
  /// Fills an arbitrary container
596
600
  template<typename Data>
597
- void fill(Data &&data, size_t size) const {
601
+ void fill(Data &&data, size_t size, double warp, bool isForSynthesis) const {
598
602
  double invSize = 1.0/size;
599
603
  double offsetScale = gaussian(1)/(gaussian(3) + gaussian(-1));
600
604
  double norm = 1/(gaussian(0) - 2*offsetScale*(gaussian(2)));
605
+ size_t offsetI = (size&1) ? 1 : (isForSynthesis ? 0 : 2);
601
606
  for (size_t i = 0; i < size; ++i) {
602
- double r = (2*i + 1)*invSize - 1;
607
+ double r = (2*i + offsetI)*invSize - 1;
608
+ r = (r + warp)/(1 + r*warp);
603
609
  data[i] = norm*(gaussian(r) - offsetScale*(gaussian(r - 2) + gaussian(r + 2)));
604
610
  }
605
611
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-api",
3
- "version": "0.7.2-nightly-c06331b-20250824",
3
+ "version": "0.7.2-nightly-799cc6b-20250825",
4
4
  "description": "react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification",
5
5
  "bin": {
6
6
  "setup-rn-audio-api-web": "./scripts/setup-rn-audio-api-web.js"
@@ -1,326 +0,0 @@
1
- // If possible, only include vecLib, since JUCE has conflicts with vImage
2
- #if defined(HAVE_ACCELERATE)
3
- #include <Accelerate/Accelerate.h>
4
-
5
- namespace signalsmith { namespace linear {
6
-
7
- namespace _impl {
8
- template<>
9
- inline void complexMul<float>(std::complex<float> *a, const std::complex<float> *b, const std::complex<float> *c, size_t size) {
10
- DSPSplitComplex aSplit = {(float *)a, (float *)a + 1};
11
- DSPSplitComplex bSplit = {(float *)b, (float *)b + 1};
12
- DSPSplitComplex cSplit = {(float *)c, (float *)c + 1};
13
- vDSP_zvmul(&cSplit, 2, &bSplit, 2, &aSplit, 2, size, 1);
14
- }
15
- template<>
16
- inline void complexMulConj<float>(std::complex<float> *a, const std::complex<float> *b, const std::complex<float> *c, size_t size) {
17
- DSPSplitComplex aSplit = {(float *)a, (float *)a + 1};
18
- DSPSplitComplex bSplit = {(float *)b, (float *)b + 1};
19
- DSPSplitComplex cSplit = {(float *)c, (float *)c + 1};
20
- vDSP_zvmul(&cSplit, 2, &bSplit, 2, &aSplit, 2, size, -1);
21
- }
22
- template<>
23
- inline void complexMul<float>(float *ar, float *ai, const float *br, const float *bi, const float *cr, const float *ci, size_t size) {
24
- DSPSplitComplex aSplit = {ar, ai};
25
- DSPSplitComplex bSplit = {(float *)br, (float *)bi};
26
- DSPSplitComplex cSplit = {(float *)cr, (float *)ci};
27
- vDSP_zvmul(&cSplit, 1, &bSplit, 1, &aSplit, 1, size, 1);
28
- }
29
- template<>
30
- inline void complexMulConj<float>(float *ar, float *ai, const float *br, const float *bi, const float *cr, const float *ci, size_t size) {
31
- DSPSplitComplex aSplit = {ar, ai};
32
- DSPSplitComplex bSplit = {(float *)br, (float *)bi};
33
- DSPSplitComplex cSplit = {(float *)cr, (float *)ci};
34
- vDSP_zvmul(&cSplit, 1, &bSplit, 1, &aSplit, 1, size, -1);
35
- }
36
-
37
- // doubles
38
- template<>
39
- inline void complexMul<double>(std::complex<double> *a, const std::complex<double> *b, const std::complex<double> *c, size_t size) {
40
- DSPDoubleSplitComplex aSplit = {(double *)a, (double *)a + 1};
41
- DSPDoubleSplitComplex bSplit = {(double *)b, (double *)b + 1};
42
- DSPDoubleSplitComplex cSplit = {(double *)c, (double *)c + 1};
43
- vDSP_zvmulD(&cSplit, 2, &bSplit, 2, &aSplit, 2, size, 1);
44
- }
45
- template<>
46
- inline void complexMulConj<double>(std::complex<double> *a, const std::complex<double> *b, const std::complex<double> *c, size_t size) {
47
- DSPDoubleSplitComplex aSplit = {(double *)a, (double *)a + 1};
48
- DSPDoubleSplitComplex bSplit = {(double *)b, (double *)b + 1};
49
- DSPDoubleSplitComplex cSplit = {(double *)c, (double *)c + 1};
50
- vDSP_zvmulD(&cSplit, 2, &bSplit, 2, &aSplit, 2, size, -1);
51
- }
52
- template<>
53
- inline void complexMul<double>(double *ar, double *ai, const double *br, const double *bi, const double *cr, const double *ci, size_t size) {
54
- DSPDoubleSplitComplex aSplit = {ar, ai};
55
- DSPDoubleSplitComplex bSplit = {(double *)br, (double *)bi};
56
- DSPDoubleSplitComplex cSplit = {(double *)cr, (double *)ci};
57
- vDSP_zvmulD(&cSplit, 1, &bSplit, 1, &aSplit, 1, size, 1);
58
- }
59
- template<>
60
- inline void complexMulConj<double>(double *ar, double *ai, const double *br, const double *bi, const double *cr, const double *ci, size_t size) {
61
- DSPDoubleSplitComplex aSplit = {ar, ai};
62
- DSPDoubleSplitComplex bSplit = {(double *)br, (double *)bi};
63
- DSPDoubleSplitComplex cSplit = {(double *)cr, (double *)ci};
64
- vDSP_zvmulD(&cSplit, 1, &bSplit, 1, &aSplit, 1, size, -1);
65
- }
66
- }
67
-
68
- template<>
69
- struct Pow2FFT<float> {
70
- static constexpr bool prefersSplit = true;
71
-
72
- using Complex = std::complex<float>;
73
-
74
- Pow2FFT(size_t size = 0) {
75
- resize(size);
76
- }
77
- ~Pow2FFT() {
78
- if (hasSetup) vDSP_destroy_fftsetup(fftSetup);
79
- }
80
-
81
- void resize(size_t size) {
82
- _size = size;
83
- if (hasSetup) vDSP_destroy_fftsetup(fftSetup);
84
- if (!size) {
85
- hasSetup = false;
86
- return;
87
- }
88
-
89
- splitReal.resize(size);
90
- splitImag.resize(size);
91
- log2 = std::round(std::log2(size));
92
- fftSetup = vDSP_create_fftsetup(log2, FFT_RADIX2);
93
- hasSetup = true;
94
- }
95
-
96
- void fft(const Complex* input, Complex* output) {
97
- DSPSplitComplex splitComplex{ splitReal.data(), splitImag.data() };
98
- vDSP_ctoz((DSPComplex*)input, 2, &splitComplex, 1, _size);
99
- vDSP_fft_zip(fftSetup, &splitComplex, 1, log2, kFFTDirection_Forward);
100
- vDSP_ztoc(&splitComplex, 1, (DSPComplex*)output, 2, _size);
101
- }
102
- void fft(const float *inR, const float *inI, float *outR, float *outI) {
103
- DSPSplitComplex inSplit{(float *)inR, (float *)inI};
104
- DSPSplitComplex outSplit{outR, outI};
105
- vDSP_fft_zop(fftSetup, &inSplit, 1, &outSplit, 1, log2, kFFTDirection_Forward);
106
- }
107
-
108
- void ifft(const Complex* input, Complex* output) {
109
- DSPSplitComplex splitComplex{ splitReal.data(), splitImag.data() };
110
- vDSP_ctoz((DSPComplex*)input, 2, &splitComplex, 1, _size);
111
- vDSP_fft_zip(fftSetup, &splitComplex, 1, log2, kFFTDirection_Inverse);
112
- vDSP_ztoc(&splitComplex, 1, (DSPComplex*)output, 2, _size);
113
- }
114
- void ifft(const float *inR, const float *inI, float *outR, float *outI) {
115
- DSPSplitComplex inSplit{(float *)inR, (float *)inI};
116
- DSPSplitComplex outSplit{outR, outI};
117
- vDSP_fft_zop(fftSetup, &inSplit, 1, &outSplit, 1, log2, kFFTDirection_Inverse);
118
- }
119
-
120
- private:
121
- size_t _size = 0;
122
- bool hasSetup = false;
123
- FFTSetup fftSetup;
124
- int log2 = 0;
125
- std::vector<float> splitReal, splitImag;
126
- };
127
-
128
- template<>
129
- struct Pow2RealFFT<float> {
130
- static constexpr bool prefersSplit = true;
131
-
132
- using Complex = std::complex<float>;
133
-
134
- Pow2RealFFT(size_t size = 0) {
135
- resize(size);
136
- }
137
- ~Pow2RealFFT() {
138
- if (hasSetup) vDSP_destroy_fftsetup(fftSetup);
139
- }
140
-
141
- void resize(size_t size) {
142
- _size = size;
143
- if (hasSetup) vDSP_destroy_fftsetup(fftSetup);
144
- if (!size) {
145
- hasSetup = false;
146
- return;
147
- }
148
-
149
- splitReal.resize(size);
150
- splitImag.resize(size);
151
- log2 = std::log2(size);
152
- fftSetup = vDSP_create_fftsetup(log2, FFT_RADIX2);
153
- hasSetup = true;
154
- }
155
-
156
- void fft(const float* input, Complex* output) {
157
- float mul = 0.5f;
158
- vDSP_vsmul(input, 2, &mul, splitReal.data(), 1, _size/2);
159
- vDSP_vsmul(input + 1, 2, &mul, splitImag.data(), 1, _size/2);
160
- DSPSplitComplex tmpSplit{splitReal.data(), splitImag.data()};
161
- vDSP_fft_zrip(fftSetup, &tmpSplit, 1, log2, kFFTDirection_Forward);
162
- vDSP_ztoc(&tmpSplit, 1, (DSPComplex *)output, 2, _size/2);
163
- }
164
- void fft(const float *inR, float *outR, float *outI) {
165
- DSPSplitComplex outputSplit{outR, outI};
166
- float mul = 0.5f;
167
- vDSP_vsmul(inR, 2, &mul, outR, 1, _size/2);
168
- vDSP_vsmul(inR + 1, 2, &mul, outI, 1, _size/2);
169
- vDSP_fft_zrip(fftSetup, &outputSplit, 1, log2, kFFTDirection_Forward);
170
- }
171
-
172
- void ifft(const Complex * input, float * output) {
173
- DSPSplitComplex tmpSplit{splitReal.data(), splitImag.data()};
174
- vDSP_ctoz((DSPComplex*)input, 2, &tmpSplit, 1, _size/2);
175
- vDSP_fft_zrip(fftSetup, &tmpSplit, 1, log2, kFFTDirection_Inverse);
176
- DSPSplitComplex outputSplit{output, output + 1};
177
- vDSP_zvmov(&tmpSplit, 1, &outputSplit, 2, _size/2);
178
- }
179
- void ifft(const float *inR, const float *inI, float *outR) {
180
- DSPSplitComplex inputSplit{(float *)inR, (float *)inI};
181
- DSPSplitComplex tmpSplit{splitReal.data(), splitImag.data()};
182
- vDSP_fft_zrop(fftSetup, &inputSplit, 1, &tmpSplit, 1, log2, kFFTDirection_Inverse);
183
- DSPSplitComplex outputSplit{outR, outR + 1};
184
- // We can't use vDSP_ztoc without knowing the alignment
185
- vDSP_zvmov(&tmpSplit, 1, &outputSplit, 2, _size/2);
186
- }
187
-
188
- private:
189
- size_t _size = 0;
190
- bool hasSetup = false;
191
- FFTSetup fftSetup;
192
- int log2 = 0;
193
- std::vector<float> splitReal, splitImag;
194
- };
195
-
196
- template<>
197
- struct Pow2FFT<double> {
198
- static constexpr bool prefersSplit = true;
199
-
200
- using Complex = std::complex<double>;
201
-
202
- Pow2FFT(size_t size=0) {
203
- resize(size);
204
- }
205
- ~Pow2FFT() {
206
- if (hasSetup) vDSP_destroy_fftsetupD(fftSetup);
207
- }
208
-
209
- void resize(size_t size) {
210
- _size = size;
211
- if (hasSetup) vDSP_destroy_fftsetupD(fftSetup);
212
- if (!size) {
213
- hasSetup = false;
214
- return;
215
- }
216
-
217
- log2 = std::round(std::log2(size));
218
- fftSetup = vDSP_create_fftsetupD(log2, FFT_RADIX2);
219
- hasSetup = true;
220
-
221
- splitReal.resize(size);
222
- splitImag.resize(size);
223
- }
224
-
225
- void fft(const Complex* input, Complex* output) {
226
- DSPDoubleSplitComplex splitComplex{ splitReal.data(), splitImag.data() };
227
- vDSP_ctozD((DSPDoubleComplex*)input, 2, &splitComplex, 1, _size);
228
- vDSP_fft_zipD(fftSetup, &splitComplex, 1, log2, kFFTDirection_Forward);
229
- vDSP_ztocD(&splitComplex, 1, (DSPDoubleComplex*)output, 2, _size);
230
- }
231
- void fft(const double *inR, const double *inI, double *outR, double *outI) {
232
- DSPDoubleSplitComplex inSplit{(double *)inR, (double *)inI};
233
- DSPDoubleSplitComplex outSplit{outR, outI};
234
- vDSP_fft_zopD(fftSetup, &inSplit, 1, &outSplit, 1, log2, kFFTDirection_Forward);
235
- }
236
-
237
- void ifft(const Complex* input, Complex* output) {
238
- DSPDoubleSplitComplex splitComplex{ splitReal.data(), splitImag.data() };
239
- vDSP_ctozD((DSPDoubleComplex*)input, 2, &splitComplex, 1, _size);
240
- vDSP_fft_zipD(fftSetup, &splitComplex, 1, log2, kFFTDirection_Inverse);
241
- vDSP_ztocD(&splitComplex, 1, (DSPDoubleComplex*)output, 2, _size);
242
- }
243
- void ifft(const double *inR, const double *inI, double *outR, double *outI) {
244
- DSPDoubleSplitComplex inSplit{(double *)inR, (double *)inI};
245
- DSPDoubleSplitComplex outSplit{outR, outI};
246
- vDSP_fft_zopD(fftSetup, &inSplit, 1, &outSplit, 1, log2, kFFTDirection_Inverse);
247
- }
248
-
249
- private:
250
- size_t _size = 0;
251
- bool hasSetup = false;
252
- FFTSetupD fftSetup;
253
- int log2 = 0;
254
- std::vector<double> splitReal, splitImag;
255
- };
256
-
257
- template<>
258
- struct Pow2RealFFT<double> {
259
- static constexpr bool prefersSplit = true;
260
-
261
- using Complex = std::complex<double>;
262
-
263
- Pow2RealFFT(size_t size = 0) {
264
- resize(size);
265
- }
266
- ~Pow2RealFFT() {
267
- if (hasSetup) vDSP_destroy_fftsetupD(fftSetup);
268
- }
269
-
270
- void resize(size_t size) {
271
- _size = size;
272
- if (hasSetup) vDSP_destroy_fftsetupD(fftSetup);
273
- if (!size) {
274
- hasSetup = false;
275
- return;
276
- }
277
-
278
- splitReal.resize(size);
279
- splitImag.resize(size);
280
- log2 = std::log2(size);
281
- fftSetup = vDSP_create_fftsetupD(log2, FFT_RADIX2);
282
- hasSetup = true;
283
- }
284
-
285
- void fft(const double* input, Complex* output) {
286
- double mul = 0.5f;
287
- vDSP_vsmulD(input, 2, &mul, splitReal.data(), 1, _size/2);
288
- vDSP_vsmulD(input + 1, 2, &mul, splitImag.data(), 1, _size/2);
289
- DSPDoubleSplitComplex tmpSplit{splitReal.data(), splitImag.data()};
290
- vDSP_fft_zripD(fftSetup, &tmpSplit, 1, log2, kFFTDirection_Forward);
291
- vDSP_ztocD(&tmpSplit, 1, (DSPDoubleComplex *)output, 2, _size/2);
292
- }
293
- void fft(const double *inR, double *outR, double *outI) {
294
- DSPDoubleSplitComplex outputSplit{outR, outI};
295
- double mul = 0.5f;
296
- vDSP_vsmulD(inR, 2, &mul, outR, 1, _size/2);
297
- vDSP_vsmulD(inR + 1, 2, &mul, outI, 1, _size/2);
298
- vDSP_fft_zripD(fftSetup, &outputSplit, 1, log2, kFFTDirection_Forward);
299
- }
300
-
301
- void ifft(const Complex * input, double * output) {
302
- DSPDoubleSplitComplex tmpSplit{splitReal.data(), splitImag.data()};
303
- vDSP_ctozD((DSPDoubleComplex*)input, 2, &tmpSplit, 1, _size/2);
304
- vDSP_fft_zripD(fftSetup, &tmpSplit, 1, log2, kFFTDirection_Inverse);
305
- DSPDoubleSplitComplex outputSplit{output, output + 1};
306
- vDSP_zvmovD(&tmpSplit, 1, &outputSplit, 2, _size/2);
307
- }
308
- void ifft(const double *inR, const double *inI, double *outR) {
309
- DSPDoubleSplitComplex inputSplit{(double *)inR, (double *)inI};
310
- DSPDoubleSplitComplex tmpSplit{splitReal.data(), splitImag.data()};
311
- vDSP_fft_zropD(fftSetup, &inputSplit, 1, &tmpSplit, 1, log2, kFFTDirection_Inverse);
312
- DSPDoubleSplitComplex outputSplit{outR, outR + 1};
313
- // We can't use vDSP_ztoc without knowing the alignment
314
- vDSP_zvmovD(&tmpSplit, 1, &outputSplit, 2, _size/2);
315
- }
316
-
317
- private:
318
- size_t _size = 0;
319
- bool hasSetup = false;
320
- FFTSetupD fftSetup;
321
- int log2 = 0;
322
- std::vector<double> splitReal, splitImag;
323
- };
324
-
325
- }} // namespace
326
- #endif