react-native-audio-api 0.4.10 → 0.4.12-beta.1

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.
Files changed (116) hide show
  1. package/android/build.gradle +2 -0
  2. package/android/src/main/cpp/{core/AudioAPIInstaller.cpp → AudioAPIModule.cpp} +12 -11
  3. package/android/src/main/cpp/{core/AudioAPIInstaller.h → AudioAPIModule.h} +9 -11
  4. package/android/src/main/cpp/OnLoad.cpp +2 -2
  5. package/android/src/main/cpp/core/AudioDecoder.cpp +5 -5
  6. package/android/src/main/cpp/core/AudioPlayer.cpp +12 -0
  7. package/android/src/main/cpp/core/AudioPlayer.h +2 -0
  8. package/android/src/main/java/com/swmansion/audioapi/{module/AudioAPIInstaller.kt → AudioAPIModule.kt} +22 -10
  9. package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +31 -6
  10. package/android/src/oldarch/NativeAudioAPIModuleSpec.java +37 -0
  11. package/common/cpp/HostObjects/AudioBufferSourceNodeHostObject.h +1 -2
  12. package/common/cpp/HostObjects/AudioContextHostObject.h +34 -1
  13. package/common/cpp/HostObjects/BaseAudioContextHostObject.h +8 -0
  14. package/common/cpp/HostObjects/GainNodeHostObject.h +2 -2
  15. package/common/cpp/HostObjects/StretcherNodeHostObject.h +35 -0
  16. package/common/cpp/core/AnalyserNode.cpp +2 -2
  17. package/common/cpp/core/AnalyserNode.h +1 -1
  18. package/common/cpp/core/AudioBuffer.cpp +4 -2
  19. package/common/cpp/core/AudioBuffer.h +1 -1
  20. package/common/cpp/core/AudioBufferSourceNode.cpp +3 -3
  21. package/common/cpp/core/AudioBufferSourceNode.h +3 -3
  22. package/common/cpp/core/AudioBus.cpp +8 -0
  23. package/common/cpp/core/AudioBus.h +3 -0
  24. package/common/cpp/core/AudioContext.cpp +10 -0
  25. package/common/cpp/core/AudioContext.h +2 -0
  26. package/common/cpp/core/AudioDecoder.h +2 -1
  27. package/common/cpp/core/AudioDestinationNode.cpp +1 -1
  28. package/common/cpp/core/AudioDestinationNode.h +1 -1
  29. package/common/cpp/core/AudioNode.cpp +10 -6
  30. package/common/cpp/core/AudioNode.h +5 -3
  31. package/common/cpp/core/AudioScheduledSourceNode.cpp +1 -1
  32. package/common/cpp/core/AudioScheduledSourceNode.h +1 -1
  33. package/common/cpp/core/BaseAudioContext.cpp +7 -0
  34. package/common/cpp/core/BaseAudioContext.h +2 -0
  35. package/common/cpp/core/BiquadFilterNode.cpp +1 -1
  36. package/common/cpp/core/BiquadFilterNode.h +1 -1
  37. package/common/cpp/core/GainNode.cpp +3 -1
  38. package/common/cpp/core/GainNode.h +1 -1
  39. package/common/cpp/core/OscillatorNode.cpp +3 -1
  40. package/common/cpp/core/OscillatorNode.h +1 -1
  41. package/common/cpp/core/StereoPannerNode.cpp +1 -1
  42. package/common/cpp/core/StereoPannerNode.h +1 -1
  43. package/common/cpp/core/StretcherNode.cpp +96 -0
  44. package/common/cpp/core/StretcherNode.h +63 -0
  45. package/common/cpp/installer/AudioAPIModuleInstaller.h +49 -0
  46. package/common/cpp/libs/dsp/LICENSE.txt +21 -0
  47. package/common/cpp/libs/dsp/README.md +40 -0
  48. package/common/cpp/libs/dsp/common.h +47 -0
  49. package/common/cpp/libs/dsp/curves.h +371 -0
  50. package/common/cpp/libs/dsp/delay.h +717 -0
  51. package/common/cpp/libs/dsp/envelopes.h +523 -0
  52. package/common/cpp/libs/dsp/fft.h +523 -0
  53. package/common/cpp/libs/dsp/filters.h +436 -0
  54. package/common/cpp/libs/dsp/mix.h +218 -0
  55. package/common/cpp/libs/dsp/perf.h +84 -0
  56. package/common/cpp/libs/dsp/rates.h +184 -0
  57. package/common/cpp/libs/dsp/spectral.h +496 -0
  58. package/common/cpp/libs/dsp/windows.h +219 -0
  59. package/common/cpp/libs/signalsmith-stretch.h +637 -0
  60. package/common/cpp/types/TimeStretchType.h +6 -0
  61. package/ios/AudioAPIModule.h +1 -1
  62. package/ios/AudioAPIModule.mm +10 -3
  63. package/ios/core/AudioDecoder.mm +2 -3
  64. package/ios/core/AudioPlayer.h +14 -0
  65. package/ios/core/AudioPlayer.m +86 -25
  66. package/ios/core/IOSAudioPlayer.h +2 -0
  67. package/ios/core/IOSAudioPlayer.mm +10 -0
  68. package/lib/module/core/AudioContext.js +7 -1
  69. package/lib/module/core/AudioContext.js.map +1 -1
  70. package/lib/module/core/BaseAudioContext.js +4 -0
  71. package/lib/module/core/BaseAudioContext.js.map +1 -1
  72. package/lib/module/core/StretcherNode.js +12 -0
  73. package/lib/module/core/StretcherNode.js.map +1 -0
  74. package/lib/module/index.js +12 -3
  75. package/lib/module/index.js.map +1 -1
  76. package/lib/module/index.web.js +1 -1
  77. package/lib/module/index.web.js.map +1 -1
  78. package/lib/module/specs/NativeAudioAPIModule.js +5 -0
  79. package/lib/module/specs/NativeAudioAPIModule.js.map +1 -0
  80. package/lib/module/web-core/AudioContext.js +6 -0
  81. package/lib/module/web-core/AudioContext.js.map +1 -1
  82. package/lib/typescript/core/AudioContext.d.ts +2 -0
  83. package/lib/typescript/core/AudioContext.d.ts.map +1 -1
  84. package/lib/typescript/core/BaseAudioContext.d.ts +2 -0
  85. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  86. package/lib/typescript/core/StretcherNode.d.ts +10 -0
  87. package/lib/typescript/core/StretcherNode.d.ts.map +1 -0
  88. package/lib/typescript/index.d.ts +5 -0
  89. package/lib/typescript/index.d.ts.map +1 -1
  90. package/lib/typescript/index.web.d.ts +1 -1
  91. package/lib/typescript/index.web.d.ts.map +1 -1
  92. package/lib/typescript/interfaces.d.ts +11 -1
  93. package/lib/typescript/interfaces.d.ts.map +1 -1
  94. package/lib/typescript/specs/NativeAudioAPIModule.d.ts +7 -0
  95. package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -0
  96. package/lib/typescript/web-core/AudioContext.d.ts +3 -1
  97. package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
  98. package/package.json +9 -7
  99. package/src/core/AudioContext.ts +9 -1
  100. package/src/core/BaseAudioContext.ts +5 -0
  101. package/src/core/StretcherNode.ts +15 -0
  102. package/src/index.ts +17 -3
  103. package/src/index.web.ts +1 -0
  104. package/src/interfaces.ts +13 -1
  105. package/src/specs/NativeAudioAPIModule.ts +7 -0
  106. package/src/web-core/AudioContext.tsx +9 -1
  107. package/android/src/main/java/com/swmansion/audioapi/nativemodules/AudioAPIModule.kt +0 -26
  108. package/common/cpp/HostObjects/AudioAPIInstallerHostObject.h +0 -56
  109. package/lib/module/specs/global.d.js +0 -4
  110. package/lib/module/specs/global.d.js.map +0 -1
  111. package/lib/module/specs/install.js +0 -18
  112. package/lib/module/specs/install.js.map +0 -1
  113. package/lib/typescript/specs/install.d.ts +0 -7
  114. package/lib/typescript/specs/install.d.ts.map +0 -1
  115. package/src/specs/global.d.ts +0 -12
  116. package/src/specs/install.ts +0 -32
@@ -0,0 +1,637 @@
1
+ #ifndef SIGNALSMITH_STRETCH_H
2
+ #define SIGNALSMITH_STRETCH_H
3
+
4
+ #include "dsp/spectral.h"
5
+ #include "dsp/delay.h"
6
+ #include "dsp/perf.h"
7
+ SIGNALSMITH_DSP_VERSION_CHECK(1, 6, 0); // Check version is compatible
8
+ #include <vector>
9
+ #include <algorithm>
10
+ #include <functional>
11
+ #include <random>
12
+
13
+ namespace signalsmith { namespace stretch {
14
+
15
+ template<typename Sample=float, class RandomEngine=std::default_random_engine>
16
+ struct SignalsmithStretch {
17
+ static constexpr size_t version[3] = {1, 1, 1};
18
+
19
+ SignalsmithStretch() : randomEngine(std::random_device{}()) {}
20
+ SignalsmithStretch(long seed) : randomEngine(seed) {}
21
+
22
+ int blockSamples() const {
23
+ return stft.windowSize();
24
+ }
25
+ int intervalSamples() const {
26
+ return stft.interval();
27
+ }
28
+ int inputLatency() const {
29
+ return stft.windowSize()/2;
30
+ }
31
+ int outputLatency() const {
32
+ return stft.windowSize() - inputLatency();
33
+ }
34
+
35
+ void reset() {
36
+ stft.reset();
37
+ inputBuffer.reset();
38
+ prevInputOffset = -1;
39
+ channelBands.assign(channelBands.size(), Band());
40
+ silenceCounter = 0;
41
+ didSeek = false;
42
+ flushed = true;
43
+ }
44
+
45
+ // Configures using a default preset
46
+ void presetDefault(int nChannels, Sample sampleRate) {
47
+ configure(nChannels, sampleRate*0.12, sampleRate*0.03);
48
+ }
49
+ void presetCheaper(int nChannels, Sample sampleRate) {
50
+ configure(nChannels, sampleRate*0.1, sampleRate*0.04);
51
+ }
52
+
53
+ // Manual setup
54
+ void configure(int nChannels, int blockSamples, int intervalSamples) {
55
+ channels = nChannels;
56
+ stft.setWindow(stft.kaiser, true);
57
+ stft.resize(channels, blockSamples, intervalSamples);
58
+ bands = stft.bands();
59
+ inputBuffer.resize(channels, blockSamples + intervalSamples + 1);
60
+ timeBuffer.assign(stft.fftSize(), 0);
61
+ channelBands.assign(bands*channels, Band());
62
+
63
+ peaks.reserve(bands/2);
64
+ energy.resize(bands);
65
+ smoothedEnergy.resize(bands);
66
+ outputMap.resize(bands);
67
+ channelPredictions.resize(channels*bands);
68
+ }
69
+
70
+ /// Frequency multiplier, and optional tonality limit (as multiple of sample-rate)
71
+ void setTransposeFactor(Sample multiplier, Sample tonalityLimit=0) {
72
+ freqMultiplier = multiplier;
73
+ if (tonalityLimit > 0) {
74
+ freqTonalityLimit = tonalityLimit/std::sqrt(multiplier); // compromise between input and output limits
75
+ } else {
76
+ freqTonalityLimit = 1;
77
+ }
78
+ customFreqMap = nullptr;
79
+ }
80
+ void setTransposeSemitones(Sample semitones, Sample tonalityLimit=0) {
81
+ setTransposeFactor(std::pow(2, semitones/12), tonalityLimit);
82
+ customFreqMap = nullptr;
83
+ }
84
+ // Sets a custom frequency map - should be monotonically increasing
85
+ void setFreqMap(std::function<Sample(Sample)> inputToOutput) {
86
+ customFreqMap = inputToOutput;
87
+ }
88
+
89
+ // Provide previous input ("pre-roll"), without affecting the speed calculation. You should ideally feed it one block-length + one interval
90
+ template<class Inputs>
91
+ void seek(Inputs &&inputs, int inputSamples, double playbackRate) {
92
+ inputBuffer.reset();
93
+ Sample totalEnergy = 0;
94
+ for (int c = 0; c < channels; ++c) {
95
+ auto &&inputChannel = inputs[c];
96
+ auto &&bufferChannel = inputBuffer[c];
97
+ int startIndex = std::max<int>(0, inputSamples - stft.windowSize() - stft.interval());
98
+ for (int i = startIndex; i < inputSamples; ++i) {
99
+ Sample s = inputChannel[i];
100
+ totalEnergy += s*s;
101
+ bufferChannel[i] = s;
102
+ }
103
+ }
104
+ if (totalEnergy >= noiseFloor) {
105
+ silenceCounter = 0;
106
+ silenceFirst = true;
107
+ }
108
+ inputBuffer += inputSamples;
109
+ didSeek = true;
110
+ seekTimeFactor = (playbackRate*stft.interval() > 1) ? 1/playbackRate : stft.interval();
111
+ }
112
+
113
+ template<class Inputs, class Outputs>
114
+ void process(Inputs &&inputs, int inputSamples, Outputs &&outputs, int outputSamples) {
115
+ Sample totalEnergy = 0;
116
+ for (int c = 0; c < channels; ++c) {
117
+ auto &&inputChannel = inputs[c];
118
+ for (int i = 0; i < inputSamples; ++i) {
119
+ Sample s = inputChannel[i];
120
+ totalEnergy += s*s;
121
+ }
122
+ }
123
+ if (totalEnergy < noiseFloor) {
124
+ if (silenceCounter >= 2*stft.windowSize()) {
125
+ if (silenceFirst) {
126
+ silenceFirst = false;
127
+ for (auto &b : channelBands) {
128
+ b.input = b.prevInput = b.output = 0;
129
+ b.inputEnergy = 0;
130
+ }
131
+ }
132
+
133
+ if (inputSamples > 0) {
134
+ // copy from the input, wrapping around if needed
135
+ for (int outputIndex = 0; outputIndex < outputSamples; ++outputIndex) {
136
+ int inputIndex = outputIndex%inputSamples;
137
+ for (int c = 0; c < channels; ++c) {
138
+ outputs[c][outputIndex] = inputs[c][inputIndex];
139
+ }
140
+ }
141
+ } else {
142
+ for (int c = 0; c < channels; ++c) {
143
+ auto &&outputChannel = outputs[c];
144
+ for (int outputIndex = 0; outputIndex < outputSamples; ++outputIndex) {
145
+ outputChannel[outputIndex] = 0;
146
+ }
147
+ }
148
+ }
149
+
150
+ // Store input in history buffer
151
+ for (int c = 0; c < channels; ++c) {
152
+ auto &&inputChannel = inputs[c];
153
+ auto &&bufferChannel = inputBuffer[c];
154
+ int startIndex = std::max<int>(0, inputSamples - stft.windowSize() - stft.interval());
155
+ for (int i = startIndex; i < inputSamples; ++i) {
156
+ bufferChannel[i] = inputChannel[i];
157
+ }
158
+ }
159
+ inputBuffer += inputSamples;
160
+ return;
161
+ } else {
162
+ silenceCounter += inputSamples;
163
+ }
164
+ } else {
165
+ silenceCounter = 0;
166
+ silenceFirst = true;
167
+ }
168
+
169
+ for (int outputIndex = 0; outputIndex < outputSamples; ++outputIndex) {
170
+ stft.ensureValid(outputIndex, [&](int outputOffset) {
171
+ // Time to process a spectrum! Where should it come from in the input?
172
+ int inputOffset = std::round(outputOffset*Sample(inputSamples)/outputSamples) - stft.windowSize();
173
+ int inputInterval = inputOffset - prevInputOffset;
174
+ prevInputOffset = inputOffset;
175
+
176
+ bool newSpectrum = didSeek || (inputInterval > 0);
177
+ if (newSpectrum) {
178
+ for (int c = 0; c < channels; ++c) {
179
+ // Copy from the history buffer, if needed
180
+ auto &&bufferChannel = inputBuffer[c];
181
+ for (int i = 0; i < -inputOffset; ++i) {
182
+ timeBuffer[i] = bufferChannel[i + inputOffset];
183
+ }
184
+ // Copy the rest from the input
185
+ auto &&inputChannel = inputs[c];
186
+ for (int i = std::max<int>(0, -inputOffset); i < stft.windowSize(); ++i) {
187
+ timeBuffer[i] = inputChannel[i + inputOffset];
188
+ }
189
+ stft.analyse(c, timeBuffer);
190
+ }
191
+ flushed = false; // TODO: first block after a flush should be gain-compensated
192
+
193
+ for (int c = 0; c < channels; ++c) {
194
+ auto channelBands = bandsForChannel(c);
195
+ auto &&spectrumBands = stft.spectrum[c];
196
+ for (int b = 0; b < bands; ++b) {
197
+ channelBands[b].input = spectrumBands[b];
198
+ }
199
+ }
200
+
201
+ if (didSeek || inputInterval != stft.interval()) { // make sure the previous input is the correct distance in the past
202
+ int prevIntervalOffset = inputOffset - stft.interval();
203
+ for (int c = 0; c < channels; ++c) {
204
+ // Copy from the history buffer, if needed
205
+ auto &&bufferChannel = inputBuffer[c];
206
+ for (int i = 0; i < std::min(-prevIntervalOffset, stft.windowSize()); ++i) {
207
+ timeBuffer[i] = bufferChannel[i + prevIntervalOffset];
208
+ }
209
+ // Copy the rest from the input
210
+ auto &&inputChannel = inputs[c];
211
+ for (int i = std::max<int>(0, -prevIntervalOffset); i < stft.windowSize(); ++i) {
212
+ timeBuffer[i] = inputChannel[i + prevIntervalOffset];
213
+ }
214
+ stft.analyse(c, timeBuffer);
215
+ }
216
+ for (int c = 0; c < channels; ++c) {
217
+ auto channelBands = bandsForChannel(c);
218
+ auto &&spectrumBands = stft.spectrum[c];
219
+ for (int b = 0; b < bands; ++b) {
220
+ channelBands[b].prevInput = spectrumBands[b];
221
+ }
222
+ }
223
+ }
224
+ }
225
+
226
+ Sample timeFactor = didSeek ? seekTimeFactor : stft.interval()/std::max<Sample>(1, inputInterval);
227
+ processSpectrum(newSpectrum, timeFactor);
228
+ didSeek = false;
229
+
230
+ for (int c = 0; c < channels; ++c) {
231
+ auto channelBands = bandsForChannel(c);
232
+ auto &&spectrumBands = stft.spectrum[c];
233
+ for (int b = 0; b < bands; ++b) {
234
+ spectrumBands[b] = channelBands[b].output;
235
+ }
236
+ }
237
+ });
238
+
239
+ for (int c = 0; c < channels; ++c) {
240
+ auto &&outputChannel = outputs[c];
241
+ auto &&stftChannel = stft[c];
242
+ outputChannel[outputIndex] = stftChannel[outputIndex];
243
+ }
244
+ }
245
+
246
+ // Store input in history buffer
247
+ for (int c = 0; c < channels; ++c) {
248
+ auto &&inputChannel = inputs[c];
249
+ auto &&bufferChannel = inputBuffer[c];
250
+ int startIndex = std::max<int>(0, inputSamples - stft.windowSize());
251
+ for (int i = startIndex; i < inputSamples; ++i) {
252
+ bufferChannel[i] = inputChannel[i];
253
+ }
254
+ }
255
+ inputBuffer += inputSamples;
256
+ stft += outputSamples;
257
+ prevInputOffset -= inputSamples;
258
+ }
259
+
260
+ // Read the remaining output, providing no further input. `outputSamples` should ideally be at least `.outputLatency()`
261
+ template<class Outputs>
262
+ void flush(Outputs &&outputs, int outputSamples) {
263
+ int plainOutput = std::min<int>(outputSamples, stft.windowSize());
264
+ int foldedBackOutput = std::min<int>(outputSamples, stft.windowSize() - plainOutput);
265
+ for (int c = 0; c < channels; ++c) {
266
+ auto &&outputChannel = outputs[c];
267
+ auto &&stftChannel = stft[c];
268
+ for (int i = 0; i < plainOutput; ++i) {
269
+ // TODO: plain output should be gain-
270
+ outputChannel[i] = stftChannel[i];
271
+ }
272
+ for (int i = 0; i < foldedBackOutput; ++i) {
273
+ outputChannel[outputSamples - 1 - i] -= stftChannel[plainOutput + i];
274
+ }
275
+ for (int i = 0; i < plainOutput + foldedBackOutput; ++i) {
276
+ stftChannel[i] = 0;
277
+ }
278
+ }
279
+ // Skip the output we just used/cleared
280
+ stft += plainOutput + foldedBackOutput;
281
+ // Reset the phase-vocoder stuff, so the next block gets a fresh start
282
+ for (int c = 0; c < channels; ++c) {
283
+ auto channelBands = bandsForChannel(c);
284
+ for (int b = 0; b < bands; ++b) {
285
+ channelBands[b].prevInput = channelBands[b].output = 0;
286
+ }
287
+ }
288
+ flushed = true;
289
+ }
290
+ private:
291
+ using Complex = std::complex<Sample>;
292
+ static constexpr Sample noiseFloor{1e-15};
293
+ static constexpr Sample maxCleanStretch{2}; // time-stretch ratio before we start randomising phases
294
+ int silenceCounter = 0;
295
+ bool silenceFirst = true;
296
+
297
+ Sample freqMultiplier = 1, freqTonalityLimit = 0.5;
298
+ std::function<Sample(Sample)> customFreqMap = nullptr;
299
+
300
+ signalsmith::spectral::STFT<Sample> stft{0, 1, 1};
301
+ signalsmith::delay::MultiBuffer<Sample> inputBuffer;
302
+ int channels = 0, bands = 0;
303
+ int prevInputOffset = -1;
304
+ std::vector<Sample> timeBuffer;
305
+ bool didSeek = false, flushed = true;
306
+ Sample seekTimeFactor = 1;
307
+
308
+ Sample bandToFreq(Sample b) const {
309
+ return (b + Sample(0.5))/stft.fftSize();
310
+ }
311
+ Sample freqToBand(Sample f) const {
312
+ return f*stft.fftSize() - Sample(0.5);
313
+ }
314
+
315
+ struct Band {
316
+ Complex input, prevInput{0};
317
+ Complex output{0};
318
+ Sample inputEnergy;
319
+ };
320
+ std::vector<Band> channelBands;
321
+ Band * bandsForChannel(int channel) {
322
+ return channelBands.data() + channel*bands;
323
+ }
324
+ template<Complex Band::*member>
325
+ Complex getBand(int channel, int index) {
326
+ if (index < 0 || index >= bands) return 0;
327
+ return channelBands[index + channel*bands].*member;
328
+ }
329
+ template<Complex Band::*member>
330
+ Complex getFractional(int channel, int lowIndex, Sample fractional) {
331
+ Complex low = getBand<member>(channel, lowIndex);
332
+ Complex high = getBand<member>(channel, lowIndex + 1);
333
+ return low + (high - low)*fractional;
334
+ }
335
+ template<Complex Band::*member>
336
+ Complex getFractional(int channel, Sample inputIndex) {
337
+ int lowIndex = std::floor(inputIndex);
338
+ Sample fracIndex = inputIndex - lowIndex;
339
+ return getFractional<member>(channel, lowIndex, fracIndex);
340
+ }
341
+ template<Sample Band::*member>
342
+ Sample getBand(int channel, int index) {
343
+ if (index < 0 || index >= bands) return 0;
344
+ return channelBands[index + channel*bands].*member;
345
+ }
346
+ template<Sample Band::*member>
347
+ Sample getFractional(int channel, int lowIndex, Sample fractional) {
348
+ Sample low = getBand<member>(channel, lowIndex);
349
+ Sample high = getBand<member>(channel, lowIndex + 1);
350
+ return low + (high - low)*fractional;
351
+ }
352
+ template<Sample Band::*member>
353
+ Sample getFractional(int channel, Sample inputIndex) {
354
+ int lowIndex = std::floor(inputIndex);
355
+ Sample fracIndex = inputIndex - lowIndex;
356
+ return getFractional<member>(channel, lowIndex, fracIndex);
357
+ }
358
+
359
+ struct Peak {
360
+ Sample input, output;
361
+ };
362
+ std::vector<Peak> peaks;
363
+ std::vector<Sample> energy, smoothedEnergy;
364
+ struct PitchMapPoint {
365
+ Sample inputBin, freqGrad;
366
+ };
367
+ std::vector<PitchMapPoint> outputMap;
368
+
369
+ struct Prediction {
370
+ Sample energy = 0;
371
+ Complex input;
372
+
373
+ Complex makeOutput(Complex phase) {
374
+ Sample phaseNorm = std::norm(phase);
375
+ if (phaseNorm <= noiseFloor) {
376
+ phase = input; // prediction is too weak, fall back to the input
377
+ phaseNorm = std::norm(input) + noiseFloor;
378
+ }
379
+ return phase*std::sqrt(energy/phaseNorm);
380
+ }
381
+ };
382
+ std::vector<Prediction> channelPredictions;
383
+ Prediction * predictionsForChannel(int c) {
384
+ return channelPredictions.data() + c*bands;
385
+ }
386
+
387
+ RandomEngine randomEngine;
388
+
389
+ void processSpectrum(bool newSpectrum, Sample timeFactor) {
390
+ timeFactor = std::max<Sample>(timeFactor, 1/maxCleanStretch);
391
+ bool randomTimeFactor = (timeFactor > maxCleanStretch);
392
+ std::uniform_real_distribution<Sample> timeFactorDist(maxCleanStretch*2*randomTimeFactor - timeFactor, timeFactor);
393
+
394
+ if (newSpectrum) {
395
+ for (int c = 0; c < channels; ++c) {
396
+ auto bins = bandsForChannel(c);
397
+
398
+ Complex rot = std::polar(Sample(1), bandToFreq(0)*stft.interval()*Sample(2*M_PI));
399
+ Sample freqStep = bandToFreq(1) - bandToFreq(0);
400
+ Complex rotStep = std::polar(Sample(1), freqStep*stft.interval()*Sample(2*M_PI));
401
+
402
+ for (int b = 0; b < bands; ++b) {
403
+ auto &bin = bins[b];
404
+ bin.output = signalsmith::perf::mul(bin.output, rot);
405
+ bin.prevInput = signalsmith::perf::mul(bin.prevInput, rot);
406
+ rot = signalsmith::perf::mul(rot, rotStep);
407
+ }
408
+ }
409
+ }
410
+
411
+ Sample smoothingBins = Sample(stft.fftSize())/stft.interval();
412
+ int longVerticalStep = std::round(smoothingBins);
413
+ if (customFreqMap || freqMultiplier != 1) {
414
+ findPeaks(smoothingBins);
415
+ updateOutputMap();
416
+ } else { // we're not pitch-shifting, so no need to find peaks etc.
417
+ for (int c = 0; c < channels; ++c) {
418
+ Band *bins = bandsForChannel(c);
419
+ for (int b = 0; b < bands; ++b) {
420
+ bins[b].inputEnergy = std::norm(bins[b].input);
421
+ }
422
+ }
423
+ for (int b = 0; b < bands; ++b) {
424
+ outputMap[b] = {Sample(b), 1};
425
+ }
426
+ }
427
+
428
+ // Preliminary output prediction from phase-vocoder
429
+ for (int c = 0; c < channels; ++c) {
430
+ Band *bins = bandsForChannel(c);
431
+ auto *predictions = predictionsForChannel(c);
432
+ for (int b = 0; b < bands; ++b) {
433
+ auto mapPoint = outputMap[b];
434
+ int lowIndex = std::floor(mapPoint.inputBin);
435
+ Sample fracIndex = mapPoint.inputBin - lowIndex;
436
+
437
+ Prediction &prediction = predictions[b];
438
+ Sample prevEnergy = prediction.energy;
439
+ prediction.energy = getFractional<&Band::inputEnergy>(c, lowIndex, fracIndex);
440
+ prediction.energy *= std::max<Sample>(0, mapPoint.freqGrad); // scale the energy according to local stretch factor
441
+ prediction.input = getFractional<&Band::input>(c, lowIndex, fracIndex);
442
+
443
+ auto &outputBin = bins[b];
444
+ Complex prevInput = getFractional<&Band::prevInput>(c, lowIndex, fracIndex);
445
+ Complex freqTwist = signalsmith::perf::mul<true>(prediction.input, prevInput);
446
+ Complex phase = signalsmith::perf::mul(outputBin.output, freqTwist);
447
+ outputBin.output = phase/(std::max(prevEnergy, prediction.energy) + noiseFloor);
448
+ }
449
+ }
450
+
451
+ // Re-predict using phase differences between frequencies
452
+ for (int b = 0; b < bands; ++b) {
453
+ // Find maximum-energy channel and calculate that
454
+ int maxChannel = 0;
455
+ Sample maxEnergy = predictionsForChannel(0)[b].energy;
456
+ for (int c = 1; c < channels; ++c) {
457
+ Sample e = predictionsForChannel(c)[b].energy;
458
+ if (e > maxEnergy) {
459
+ maxChannel = c;
460
+ maxEnergy = e;
461
+ }
462
+ }
463
+
464
+ auto *predictions = predictionsForChannel(maxChannel);
465
+ auto &prediction = predictions[b];
466
+ auto *bins = bandsForChannel(maxChannel);
467
+ auto &outputBin = bins[b];
468
+
469
+ Complex phase = 0;
470
+ auto mapPoint = outputMap[b];
471
+
472
+ // Upwards vertical steps
473
+ if (b > 0) {
474
+ Sample binTimeFactor = randomTimeFactor ? timeFactorDist(randomEngine) : timeFactor;
475
+ Complex downInput = getFractional<&Band::input>(maxChannel, mapPoint.inputBin - binTimeFactor);
476
+ Complex shortVerticalTwist = signalsmith::perf::mul<true>(prediction.input, downInput);
477
+
478
+ auto &downBin = bins[b - 1];
479
+ phase += signalsmith::perf::mul(downBin.output, shortVerticalTwist);
480
+
481
+ if (b >= longVerticalStep) {
482
+ Complex longDownInput = getFractional<&Band::input>(maxChannel, mapPoint.inputBin - longVerticalStep*binTimeFactor);
483
+ Complex longVerticalTwist = signalsmith::perf::mul<true>(prediction.input, longDownInput);
484
+
485
+ auto &longDownBin = bins[b - longVerticalStep];
486
+ phase += signalsmith::perf::mul(longDownBin.output, longVerticalTwist);
487
+ }
488
+ }
489
+ // Downwards vertical steps
490
+ if (b < bands - 1) {
491
+ auto &upPrediction = predictions[b + 1];
492
+ auto &upMapPoint = outputMap[b + 1];
493
+
494
+ Sample binTimeFactor = randomTimeFactor ? timeFactorDist(randomEngine) : timeFactor;
495
+ Complex downInput = getFractional<&Band::input>(maxChannel, upMapPoint.inputBin - binTimeFactor);
496
+ Complex shortVerticalTwist = signalsmith::perf::mul<true>(upPrediction.input, downInput);
497
+
498
+ auto &upBin = bins[b + 1];
499
+ phase += signalsmith::perf::mul<true>(upBin.output, shortVerticalTwist);
500
+
501
+ if (b < bands - longVerticalStep) {
502
+ auto &longUpPrediction = predictions[b + longVerticalStep];
503
+ auto &longUpMapPoint = outputMap[b + longVerticalStep];
504
+
505
+ Complex longDownInput = getFractional<&Band::input>(maxChannel, longUpMapPoint.inputBin - longVerticalStep*binTimeFactor);
506
+ Complex longVerticalTwist = signalsmith::perf::mul<true>(longUpPrediction.input, longDownInput);
507
+
508
+ auto &longUpBin = bins[b + longVerticalStep];
509
+ phase += signalsmith::perf::mul<true>(longUpBin.output, longVerticalTwist);
510
+ }
511
+ }
512
+
513
+ outputBin.output = prediction.makeOutput(phase);
514
+
515
+ // All other bins are locked in phase
516
+ for (int c = 0; c < channels; ++c) {
517
+ if (c != maxChannel) {
518
+ auto &channelBin = bandsForChannel(c)[b];
519
+ auto &channelPrediction = predictionsForChannel(c)[b];
520
+
521
+ Complex channelTwist = signalsmith::perf::mul<true>(channelPrediction.input, prediction.input);
522
+ Complex channelPhase = signalsmith::perf::mul(outputBin.output, channelTwist);
523
+ channelBin.output = channelPrediction.makeOutput(channelPhase);
524
+ }
525
+ }
526
+ }
527
+
528
+ if (newSpectrum) {
529
+ for (auto &bin : channelBands) {
530
+ bin.prevInput = bin.input;
531
+ }
532
+ }
533
+ }
534
+
535
+ // Produces smoothed energy across all channels
536
+ void smoothEnergy(Sample smoothingBins) {
537
+ Sample smoothingSlew = 1/(1 + smoothingBins*Sample(0.5));
538
+ for (auto &e : energy) e = 0;
539
+ for (int c = 0; c < channels; ++c) {
540
+ Band *bins = bandsForChannel(c);
541
+ for (int b = 0; b < bands; ++b) {
542
+ Sample e = std::norm(bins[b].input);
543
+ bins[b].inputEnergy = e; // Used for interpolating prediction energy
544
+ energy[b] += e;
545
+ }
546
+ }
547
+ for (int b = 0; b < bands; ++b) {
548
+ smoothedEnergy[b] = energy[b];
549
+ }
550
+ Sample e = 0;
551
+ for (int repeat = 0; repeat < 2; ++repeat) {
552
+ for (int b = bands - 1; b >= 0; --b) {
553
+ e += (smoothedEnergy[b] - e)*smoothingSlew;
554
+ smoothedEnergy[b] = e;
555
+ }
556
+ for (int b = 0; b < bands; ++b) {
557
+ e += (smoothedEnergy[b] - e)*smoothingSlew;
558
+ smoothedEnergy[b] = e;
559
+ }
560
+ }
561
+ }
562
+
563
+ Sample mapFreq(Sample freq) const {
564
+ if (customFreqMap) return customFreqMap(freq);
565
+ if (freq > freqTonalityLimit) {
566
+ Sample diff = freq - freqTonalityLimit;
567
+ return freqTonalityLimit*freqMultiplier + diff;
568
+ }
569
+ return freq*freqMultiplier;
570
+ }
571
+
572
+ // Identifies spectral peaks using energy across all channels
573
+ void findPeaks(Sample smoothingBins) {
574
+ smoothEnergy(smoothingBins);
575
+
576
+ peaks.resize(0);
577
+
578
+ int start = 0;
579
+ while (start < bands) {
580
+ if (energy[start] > smoothedEnergy[start]) {
581
+ int end = start;
582
+ Sample bandSum = 0, energySum = 0;
583
+ while (end < bands && energy[end] > smoothedEnergy[end]) {
584
+ bandSum += end*energy[end];
585
+ energySum += energy[end];
586
+ ++end;
587
+ }
588
+ Sample avgBand = bandSum/energySum;
589
+ Sample avgFreq = bandToFreq(avgBand);
590
+ peaks.emplace_back(Peak{avgBand, freqToBand(mapFreq(avgFreq))});
591
+
592
+ start = end;
593
+ }
594
+ ++start;
595
+ }
596
+ }
597
+
598
+ void updateOutputMap() {
599
+ if (peaks.empty()) {
600
+ for (int b = 0; b < bands; ++b) {
601
+ outputMap[b] = {Sample(b), 1};
602
+ }
603
+ return;
604
+ }
605
+ Sample bottomOffset = peaks[0].input - peaks[0].output;
606
+ for (int b = 0; b < std::min<int>(bands, std::ceil(peaks[0].output)); ++b) {
607
+ outputMap[b] = {b + bottomOffset, 1};
608
+ }
609
+ // Interpolate between points
610
+ for (size_t p = 1; p < peaks.size(); ++p) {
611
+ const Peak &prev = peaks[p - 1], &next = peaks[p];
612
+ Sample rangeScale = 1/(next.output - prev.output);
613
+ Sample outOffset = prev.input - prev.output;
614
+ Sample outScale = next.input - next.output - prev.input + prev.output;
615
+ Sample gradScale = outScale*rangeScale;
616
+ int startBin = std::max<int>(0, std::ceil(prev.output));
617
+ int endBin = std::min<int>(bands, std::ceil(next.output));
618
+ for (int b = startBin; b < endBin; ++b) {
619
+ Sample r = (b - prev.output)*rangeScale;
620
+ Sample h = r*r*(3 - 2*r);
621
+ Sample outB = b + outOffset + h*outScale;
622
+
623
+ Sample gradH = 6*r*(1 - r);
624
+ Sample gradB = 1 + gradH*gradScale;
625
+
626
+ outputMap[b] = {outB, gradB};
627
+ }
628
+ }
629
+ Sample topOffset = peaks.back().input - peaks.back().output;
630
+ for (int b = std::max<int>(0, peaks.back().output); b < bands; ++b) {
631
+ outputMap[b] = {b + topOffset, 1};
632
+ }
633
+ }
634
+ };
635
+
636
+ }} // namespace
637
+ #endif // include guard
@@ -0,0 +1,6 @@
1
+ #pragma once
2
+
3
+ namespace audioapi {
4
+
5
+ enum class TimeStretchType {LINEAR, SPEECH, MUSIC};
6
+ } // namespace audioapi
@@ -9,7 +9,7 @@
9
9
 
10
10
  @interface AudioAPIModule : RCTEventEmitter
11
11
  #ifdef RCT_NEW_ARCH_ENABLED
12
- <RCTCallInvokerModule>
12
+ <NativeAudioAPIModuleSpec, RCTCallInvokerModule>
13
13
  #else
14
14
  <RCTBridgeModule>
15
15
  #endif // RCT_NEW_ARCH_ENABLED
@@ -6,7 +6,7 @@
6
6
  #import <ReactCommon/RCTTurboModule.h>
7
7
  #endif // RCT_NEW_ARCH_ENABLED
8
8
 
9
- #import "AudioAPIInstallerHostObject.h"
9
+ #include "AudioAPIModuleInstaller.h"
10
10
 
11
11
  @implementation AudioAPIModule
12
12
 
@@ -38,11 +38,18 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)
38
38
 
39
39
  assert(jsiRuntime != nullptr);
40
40
 
41
- auto hostObject = std::make_shared<audioapi::AudioAPIInstallerHostObject>(jsiRuntime, jsCallInvoker);
42
- hostObject->install();
41
+ audioapi::AudioAPIModuleInstaller::injectJSIBindings(jsiRuntime, jsCallInvoker);
43
42
 
44
43
  NSLog(@"Successfully installed JSI bindings for react-native-audio-api!");
45
44
  return @true;
46
45
  }
47
46
 
47
+ #ifdef RCT_NEW_ARCH_ENABLED
48
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
49
+ (const facebook::react::ObjCTurboModule::InitParams &)params
50
+ {
51
+ return std::make_shared<facebook::react::NativeAudioAPIModuleSpecJSI>(params);
52
+ }
53
+ #endif // RCT_NEW_ARCH_ENABLED
54
+
48
55
  @end
@@ -7,7 +7,7 @@
7
7
 
8
8
  namespace audioapi {
9
9
 
10
- AudioBus *AudioDecoder::decodeWithFilePath(const std::string &path) const
10
+ std::shared_ptr<AudioBus> AudioDecoder::decodeWithFilePath(const std::string &path) const
11
11
  {
12
12
  ma_decoder decoder;
13
13
  ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, sampleRate_);
@@ -23,7 +23,7 @@ AudioBus *AudioDecoder::decodeWithFilePath(const std::string &path) const
23
23
  ma_uint64 totalFrameCount;
24
24
  ma_decoder_get_length_in_pcm_frames(&decoder, &totalFrameCount);
25
25
 
26
- auto *audioBus = new AudioBus(static_cast<int>(totalFrameCount), 2, sampleRate_);
26
+ auto audioBus = std::make_shared<AudioBus>(static_cast<int>(totalFrameCount), 2, sampleRate_);
27
27
  auto *buffer = new float[totalFrameCount * 2];
28
28
 
29
29
  ma_uint64 framesDecoded;
@@ -32,7 +32,6 @@ AudioBus *AudioDecoder::decodeWithFilePath(const std::string &path) const
32
32
  NSLog(@"Failed to decode audio file: %s", path.c_str());
33
33
 
34
34
  delete[] buffer;
35
- delete audioBus;
36
35
  ma_decoder_uninit(&decoder);
37
36
 
38
37
  return nullptr;