react-native-audio-api 0.7.2-nightly-c06331b-20250823 → 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.
- 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
|
@@ -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,
|
|
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(),
|
|
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,
|
|
404
|
+
fft.ifft(step, spectrumPtr, timeBuffer.data());
|
|
339
405
|
return;
|
|
340
406
|
}
|
|
341
407
|
} else {
|
|
342
|
-
fft.ifft(
|
|
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 +
|
|
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 +
|
|
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-
|
|
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
|