react-native-audio-api 0.5.5 → 0.6.0-rc.0
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/RNAudioAPI.podspec +1 -1
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +0 -20
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +0 -2
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +13 -0
- package/android/src/main/java/com/swmansion/audioapi/AudioManagerModule.kt +59 -0
- package/android/src/oldarch/NativeAudioManagerModuleSpec.java +99 -0
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +30 -6
- package/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.h +70 -0
- package/common/cpp/audioapi/core/AudioContext.cpp +1 -12
- package/common/cpp/audioapi/core/AudioContext.h +0 -1
- package/common/cpp/audioapi/core/OfflineAudioContext.cpp +117 -0
- package/common/cpp/audioapi/core/OfflineAudioContext.h +40 -0
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +3 -3
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +28 -2
- package/common/cpp/audioapi/core/utils/AudioNodeDestructor.cpp +53 -0
- package/common/cpp/audioapi/core/utils/AudioNodeDestructor.h +33 -0
- package/common/cpp/audioapi/core/utils/AudioNodeManager.cpp +13 -10
- package/common/cpp/audioapi/core/utils/AudioNodeManager.h +3 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/fft-accelerate.h +326 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/fft.h +1257 -413
- package/common/cpp/audioapi/libs/signalsmith-stretch/signalsmith-stretch.h +398 -232
- package/common/cpp/audioapi/libs/signalsmith-stretch/stft.h +625 -0
- package/ios/audioapi/ios/AudioAPIModule.mm +2 -3
- package/ios/audioapi/ios/AudioManagerModule.h +18 -0
- package/ios/audioapi/ios/AudioManagerModule.mm +92 -0
- package/ios/audioapi/ios/core/AudioPlayer.h +4 -12
- package/ios/audioapi/ios/core/AudioPlayer.m +26 -108
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -3
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +4 -28
- package/ios/audioapi/ios/system/AudioEngine.h +23 -0
- package/ios/audioapi/ios/system/AudioEngine.mm +137 -0
- package/ios/audioapi/ios/system/AudioSessionManager.h +22 -0
- package/ios/audioapi/ios/system/AudioSessionManager.mm +183 -0
- package/ios/audioapi/ios/system/LockScreenManager.h +23 -0
- package/ios/audioapi/ios/system/LockScreenManager.mm +299 -0
- package/ios/audioapi/ios/system/NotificationManager.h +16 -0
- package/ios/audioapi/ios/system/NotificationManager.mm +151 -0
- package/lib/module/api.js +3 -1
- package/lib/module/api.js.map +1 -1
- package/lib/module/api.web.js +1 -0
- package/lib/module/api.web.js.map +1 -1
- package/lib/module/core/AudioContext.js +2 -1
- package/lib/module/core/AudioContext.js.map +1 -1
- package/lib/module/core/OfflineAudioContext.js +57 -0
- package/lib/module/core/OfflineAudioContext.js.map +1 -0
- package/lib/module/specs/NativeAudioManagerModule.js +31 -0
- package/lib/module/specs/NativeAudioManagerModule.js.map +1 -0
- package/lib/module/specs/index.js +6 -0
- package/lib/module/specs/index.js.map +1 -0
- package/lib/module/system/AudioManager.js +66 -0
- package/lib/module/system/AudioManager.js.map +1 -0
- package/lib/module/system/index.js +4 -0
- package/lib/module/system/index.js.map +1 -0
- package/lib/module/system/types.js +2 -0
- package/lib/module/system/types.js.map +1 -0
- package/lib/module/web-core/OfflineAudioContext.js +90 -0
- package/lib/module/web-core/OfflineAudioContext.js.map +1 -0
- package/lib/typescript/api.d.ts +4 -1
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/api.web.d.ts +1 -0
- package/lib/typescript/api.web.d.ts.map +1 -1
- package/lib/typescript/core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/core/OfflineAudioContext.d.ts +14 -0
- package/lib/typescript/core/OfflineAudioContext.d.ts.map +1 -0
- package/lib/typescript/interfaces.d.ts +6 -0
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioManagerModule.d.ts +13 -0
- package/lib/typescript/specs/NativeAudioManagerModule.d.ts.map +1 -0
- package/lib/typescript/specs/index.d.ts +4 -0
- package/lib/typescript/specs/index.d.ts.map +1 -0
- package/lib/typescript/system/AudioManager.d.ts +12 -0
- package/lib/typescript/system/AudioManager.d.ts.map +1 -0
- package/lib/typescript/system/index.d.ts +2 -0
- package/lib/typescript/system/index.d.ts.map +1 -0
- package/lib/typescript/system/types.d.ts +28 -0
- package/lib/typescript/system/types.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +5 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/web-core/OfflineAudioContext.d.ts +34 -0
- package/lib/typescript/web-core/OfflineAudioContext.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/api.ts +12 -2
- package/src/api.web.ts +1 -0
- package/src/core/AudioContext.ts +6 -1
- package/src/core/OfflineAudioContext.ts +94 -0
- package/src/interfaces.ts +11 -0
- package/src/specs/NativeAudioManagerModule.ts +51 -0
- package/src/specs/index.ts +6 -0
- package/src/system/AudioManager.ts +122 -0
- package/src/system/index.ts +1 -0
- package/src/system/types.ts +68 -0
- package/src/types.ts +6 -0
- package/src/web-core/OfflineAudioContext.tsx +163 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/delay.h +0 -715
- package/common/cpp/audioapi/libs/signalsmith-stretch/perf.h +0 -82
- package/common/cpp/audioapi/libs/signalsmith-stretch/spectral.h +0 -493
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
#ifndef SIGNALSMITH_AUDIO_LINEAR_STFT_H
|
|
2
|
+
#define SIGNALSMITH_AUDIO_LINEAR_STFT_H
|
|
3
|
+
|
|
4
|
+
#include <audioapi/libs/signalsmith-stretch/fft.h>
|
|
5
|
+
|
|
6
|
+
namespace signalsmith { namespace linear {
|
|
7
|
+
|
|
8
|
+
/// A self-normalising STFT, with variable position/window for output blocks
|
|
9
|
+
template<typename Sample, bool splitComputation=false, bool modified=false>
|
|
10
|
+
struct DynamicSTFT {
|
|
11
|
+
RealFFT<Sample, splitComputation, modified> fft;
|
|
12
|
+
|
|
13
|
+
using Complex = std::complex<Sample>;
|
|
14
|
+
|
|
15
|
+
enum class WindowShape {ignore, acg, kaiser};
|
|
16
|
+
static constexpr WindowShape acg = WindowShape::acg;
|
|
17
|
+
static constexpr WindowShape kaiser = WindowShape::kaiser;
|
|
18
|
+
|
|
19
|
+
void configure(size_t inChannels, size_t outChannels, size_t blockSamples, size_t extraInputHistory=0, size_t intervalSamples=0) {
|
|
20
|
+
_analysisChannels = inChannels;
|
|
21
|
+
_synthesisChannels = outChannels;
|
|
22
|
+
_blockSamples = blockSamples;
|
|
23
|
+
_fftSamples = fft.fastSizeAbove((blockSamples + 1)/2)*2;
|
|
24
|
+
fft.resize(_fftSamples);
|
|
25
|
+
_fftBins = _fftSamples/2;
|
|
26
|
+
|
|
27
|
+
_inputLengthSamples = _blockSamples + extraInputHistory;
|
|
28
|
+
input.buffer.resize(_inputLengthSamples*_analysisChannels);
|
|
29
|
+
|
|
30
|
+
output.buffer.resize(_blockSamples*_synthesisChannels);
|
|
31
|
+
output.windowProducts.resize(_blockSamples);
|
|
32
|
+
spectrumBuffer.resize(_fftBins*std::max(_analysisChannels, _synthesisChannels));
|
|
33
|
+
timeBuffer.resize(_fftSamples);
|
|
34
|
+
|
|
35
|
+
_analysisWindow.resize(_blockSamples);
|
|
36
|
+
_synthesisWindow.resize(_blockSamples);
|
|
37
|
+
setInterval(intervalSamples ? intervalSamples : blockSamples/4, acg);
|
|
38
|
+
|
|
39
|
+
reset();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
size_t blockSamples() const {
|
|
43
|
+
return _blockSamples;
|
|
44
|
+
}
|
|
45
|
+
size_t fftSamples() const {
|
|
46
|
+
return _fftSamples;
|
|
47
|
+
}
|
|
48
|
+
size_t defaultInterval() const {
|
|
49
|
+
return _defaultInterval;
|
|
50
|
+
}
|
|
51
|
+
size_t bands() const {
|
|
52
|
+
return _fftBins;
|
|
53
|
+
}
|
|
54
|
+
size_t analysisLatency() const {
|
|
55
|
+
return _blockSamples - _analysisOffset;
|
|
56
|
+
}
|
|
57
|
+
size_t synthesisLatency() const {
|
|
58
|
+
return _synthesisOffset;
|
|
59
|
+
}
|
|
60
|
+
size_t latency() const {
|
|
61
|
+
return synthesisLatency() + analysisLatency();
|
|
62
|
+
}
|
|
63
|
+
Sample binToFreq(Sample b) const {
|
|
64
|
+
return (modified ? b + Sample(0.5) : b)/_fftSamples;
|
|
65
|
+
}
|
|
66
|
+
Sample freqToBin(Sample f) const {
|
|
67
|
+
return modified ? f*_fftSamples - Sample(0.5) : f*_fftSamples;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
void reset(Sample productWeight=1) {
|
|
71
|
+
input.pos = _blockSamples;
|
|
72
|
+
output.pos = 0;
|
|
73
|
+
for (auto &v : input.buffer) v = 0;
|
|
74
|
+
for (auto &v : output.buffer) v = 0;
|
|
75
|
+
for (auto &v : spectrumBuffer) v = 0;
|
|
76
|
+
for (auto &v : output.windowProducts) v = 0;
|
|
77
|
+
addWindowProduct();
|
|
78
|
+
for (int i = int(_blockSamples) - int(_defaultInterval) - 1; i >= 0; --i) {
|
|
79
|
+
output.windowProducts[i] += output.windowProducts[i + _defaultInterval];
|
|
80
|
+
}
|
|
81
|
+
for (auto &v : output.windowProducts) v = v*productWeight + almostZero;
|
|
82
|
+
moveOutput(_defaultInterval); // ready for first block immediately
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void writeInput(size_t channel, size_t offset, size_t length, Sample *inputArray) {
|
|
86
|
+
Sample *buffer = input.buffer.data() + channel*_inputLengthSamples;
|
|
87
|
+
|
|
88
|
+
size_t offsetPos = (input.pos + offset)%_inputLengthSamples;
|
|
89
|
+
size_t inputWrapIndex = _inputLengthSamples - offsetPos;
|
|
90
|
+
size_t chunk1 = std::min(length, inputWrapIndex);
|
|
91
|
+
for (size_t i = 0; i < chunk1; ++i) {
|
|
92
|
+
size_t i2 = offsetPos + i;
|
|
93
|
+
buffer[i2] = inputArray[i];
|
|
94
|
+
}
|
|
95
|
+
for (size_t i = chunk1; i < length; ++i) {
|
|
96
|
+
size_t i2 = i + offsetPos -_inputLengthSamples;
|
|
97
|
+
buffer[i2] = inputArray[i];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
void writeInput(size_t channel, size_t length, Sample *inputArray) {
|
|
101
|
+
writeInput(channel, 0, length, inputArray);
|
|
102
|
+
}
|
|
103
|
+
void moveInput(size_t samples, bool clearInput=false) {
|
|
104
|
+
if (clearInput) {
|
|
105
|
+
size_t inputWrapIndex = _inputLengthSamples - input.pos;
|
|
106
|
+
size_t chunk1 = std::min(samples, inputWrapIndex);
|
|
107
|
+
for (size_t c = 0; c < _analysisChannels; ++c) {
|
|
108
|
+
Sample *buffer = input.buffer.data() + c*_inputLengthSamples;
|
|
109
|
+
for (size_t i = 0; i < chunk1; ++i) {
|
|
110
|
+
size_t i2 = input.pos + i;
|
|
111
|
+
buffer[i2] = 0;
|
|
112
|
+
}
|
|
113
|
+
for (size_t i = chunk1; i < samples; ++i) {
|
|
114
|
+
size_t i2 = i + input.pos - _inputLengthSamples;
|
|
115
|
+
buffer[i2] = 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
input.pos = (input.pos + samples)%_inputLengthSamples;
|
|
121
|
+
_samplesSinceAnalysis += samples;
|
|
122
|
+
}
|
|
123
|
+
size_t samplesSinceAnalysis() const {
|
|
124
|
+
return _samplesSinceAnalysis;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// When no more synthesis is expected, let output taper away to 0 based on windowing. Otherwise, the output will be scaled as if there's just a very long block interval, which can exaggerate artefacts and numerical errors. You still can't read more than `blockSamples()` into the future.
|
|
128
|
+
void finishOutput(Sample strength=1, size_t offset=0) {
|
|
129
|
+
Sample maxWindowProduct = 0;
|
|
130
|
+
|
|
131
|
+
size_t chunk1 = std::max(offset, std::min(_blockSamples, _blockSamples - output.pos));
|
|
132
|
+
|
|
133
|
+
for (size_t i = offset; i < chunk1; ++i) {
|
|
134
|
+
size_t i2 = output.pos + i;
|
|
135
|
+
Sample &wp = output.windowProducts[i2];
|
|
136
|
+
maxWindowProduct = std::max(wp, maxWindowProduct);
|
|
137
|
+
wp += (maxWindowProduct - wp)*strength;
|
|
138
|
+
}
|
|
139
|
+
for (size_t i = chunk1; i < _blockSamples; ++i) {
|
|
140
|
+
size_t i2 = i + output.pos - _blockSamples;
|
|
141
|
+
Sample &wp = output.windowProducts[i2];
|
|
142
|
+
maxWindowProduct = std::max(wp, maxWindowProduct);
|
|
143
|
+
wp += (maxWindowProduct - wp)*strength;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
void readOutput(size_t channel, size_t offset, size_t length, Sample *outputArray) {
|
|
148
|
+
Sample *buffer = output.buffer.data() + channel*_blockSamples;
|
|
149
|
+
size_t offsetPos = (output.pos + offset)%_blockSamples;
|
|
150
|
+
size_t outputWrapIndex = _blockSamples - offsetPos;
|
|
151
|
+
size_t chunk1 = std::min(length, outputWrapIndex);
|
|
152
|
+
for (size_t i = 0; i < chunk1; ++i) {
|
|
153
|
+
size_t i2 = offsetPos + i;
|
|
154
|
+
outputArray[i] = buffer[i2]/output.windowProducts[i2];
|
|
155
|
+
}
|
|
156
|
+
for (size_t i = chunk1; i < length; ++i) {
|
|
157
|
+
size_t i2 = i + offsetPos - _blockSamples;
|
|
158
|
+
outputArray[i] = buffer[i2]/output.windowProducts[i2];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
void readOutput(size_t channel, size_t length, Sample *outputArray) {
|
|
162
|
+
return readOutput(channel, 0, length, outputArray);
|
|
163
|
+
}
|
|
164
|
+
void moveOutput(size_t samples) {
|
|
165
|
+
if (samples == 1) { // avoid all the loops/chunks if we can
|
|
166
|
+
for (size_t c = 0; c < _synthesisChannels; ++c) {
|
|
167
|
+
output.buffer[output.pos + c*_blockSamples] = 0;
|
|
168
|
+
}
|
|
169
|
+
output.windowProducts[output.pos] = almostZero;
|
|
170
|
+
if (++output.pos >= _blockSamples) output.pos = 0;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// Zero the output buffer as we cross it
|
|
174
|
+
size_t outputWrapIndex = _blockSamples - output.pos;
|
|
175
|
+
size_t chunk1 = std::min(samples, outputWrapIndex);
|
|
176
|
+
for (size_t c = 0; c < _synthesisChannels; ++c) {
|
|
177
|
+
Sample *buffer = output.buffer.data() + c*_blockSamples;
|
|
178
|
+
for (size_t i = 0; i < chunk1; ++i) {
|
|
179
|
+
size_t i2 = output.pos + i;
|
|
180
|
+
buffer[i2] = 0;
|
|
181
|
+
}
|
|
182
|
+
for (size_t i = chunk1; i < samples; ++i) {
|
|
183
|
+
size_t i2 = i + output.pos - _blockSamples;
|
|
184
|
+
buffer[i2] = 0;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
for (size_t i = 0; i < chunk1; ++i) {
|
|
188
|
+
size_t i2 = output.pos + i;
|
|
189
|
+
output.windowProducts[i2] = almostZero;
|
|
190
|
+
}
|
|
191
|
+
for (size_t i = chunk1; i < samples; ++i) {
|
|
192
|
+
size_t i2 = i + output.pos - _blockSamples;
|
|
193
|
+
output.windowProducts[i2] = almostZero;
|
|
194
|
+
}
|
|
195
|
+
output.pos = (output.pos + samples)%_blockSamples;
|
|
196
|
+
_samplesSinceSynthesis += samples;
|
|
197
|
+
}
|
|
198
|
+
size_t samplesSinceSynthesis() const {
|
|
199
|
+
return _samplesSinceSynthesis;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
Complex * spectrum(size_t channel) {
|
|
203
|
+
return spectrumBuffer.data() + channel*_fftBins;
|
|
204
|
+
}
|
|
205
|
+
const Complex * spectrum(size_t channel) const {
|
|
206
|
+
return spectrumBuffer.data() + channel*_fftBins;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
Sample * analysisWindow() {
|
|
210
|
+
return _analysisWindow.data();
|
|
211
|
+
}
|
|
212
|
+
const Sample * analysisWindow() const {
|
|
213
|
+
return _analysisWindow.data();
|
|
214
|
+
}
|
|
215
|
+
// Sets the centre index of the window
|
|
216
|
+
void analysisOffset(size_t offset) {
|
|
217
|
+
_analysisOffset = offset;
|
|
218
|
+
}
|
|
219
|
+
size_t analysisOffset() const {
|
|
220
|
+
return _analysisOffset;
|
|
221
|
+
}
|
|
222
|
+
Sample * synthesisWindow() {
|
|
223
|
+
return _synthesisWindow.data();
|
|
224
|
+
}
|
|
225
|
+
const Sample * synthesisWindow() const {
|
|
226
|
+
return _synthesisWindow.data();
|
|
227
|
+
}
|
|
228
|
+
// Sets the centre index of the window
|
|
229
|
+
void synthesisOffset(size_t offset) {
|
|
230
|
+
_synthesisOffset = offset;
|
|
231
|
+
}
|
|
232
|
+
size_t synthesisOffset() const {
|
|
233
|
+
return _synthesisOffset;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
void setInterval(size_t defaultInterval, WindowShape windowShape=WindowShape::ignore) {
|
|
237
|
+
_defaultInterval = defaultInterval;
|
|
238
|
+
if (windowShape == WindowShape::ignore) return;
|
|
239
|
+
|
|
240
|
+
_analysisOffset = _synthesisOffset = _blockSamples/2;
|
|
241
|
+
|
|
242
|
+
if (windowShape == acg) {
|
|
243
|
+
auto window = ApproximateConfinedGaussian::withBandwidth(double(_blockSamples)/defaultInterval);
|
|
244
|
+
window.fill(_synthesisWindow, _blockSamples);
|
|
245
|
+
} else if (windowShape == kaiser) {
|
|
246
|
+
auto window = Kaiser::withBandwidth(double(_blockSamples)/defaultInterval, true);
|
|
247
|
+
window.fill(_synthesisWindow, _blockSamples);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (_analysisChannels == 0) {
|
|
251
|
+
for (auto &v : _analysisWindow) v = 1;
|
|
252
|
+
} else {
|
|
253
|
+
forcePerfectReconstruction(_synthesisWindow, _blockSamples, _defaultInterval);
|
|
254
|
+
for (size_t i = 0; i < _blockSamples; ++i) {
|
|
255
|
+
_analysisWindow[i] = _synthesisWindow[i];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
void analyse(size_t samplesInPast=0) {
|
|
261
|
+
for (size_t s = 0; s < analyseSteps(); ++s) {
|
|
262
|
+
analyseStep(s, samplesInPast);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
size_t analyseSteps() const {
|
|
266
|
+
return splitComputation ? _analysisChannels*(fft.steps() + 1) : _analysisChannels;
|
|
267
|
+
}
|
|
268
|
+
void analyseStep(size_t step, std::size_t samplesInPast=0) {
|
|
269
|
+
size_t fftSteps = splitComputation ? fft.steps() : 0;
|
|
270
|
+
size_t channel = step/(fftSteps + 1);
|
|
271
|
+
step -= channel*(fftSteps + 1);
|
|
272
|
+
|
|
273
|
+
if (step-- == 0) { // extra step at start of each channel: copy windowed input into buffer
|
|
274
|
+
size_t offsetPos = (_inputLengthSamples*2 + input.pos - _blockSamples - samplesInPast)%_inputLengthSamples;
|
|
275
|
+
size_t inputWrapIndex = _inputLengthSamples - offsetPos;
|
|
276
|
+
size_t chunk1 = std::min(_analysisOffset, inputWrapIndex);
|
|
277
|
+
size_t chunk2 = std::max(_analysisOffset, std::min(_blockSamples, inputWrapIndex));
|
|
278
|
+
|
|
279
|
+
_samplesSinceAnalysis = samplesInPast;
|
|
280
|
+
Sample *buffer = input.buffer.data() + channel*_inputLengthSamples;
|
|
281
|
+
for (size_t i = 0; i < chunk1; ++i) {
|
|
282
|
+
Sample w = modified ? -_analysisWindow[i] : _analysisWindow[i];
|
|
283
|
+
size_t ti = i + (_fftSamples - _analysisOffset);
|
|
284
|
+
size_t bi = offsetPos + i;
|
|
285
|
+
timeBuffer[ti] = buffer[bi]*w;
|
|
286
|
+
}
|
|
287
|
+
for (size_t i = chunk1; i < _analysisOffset; ++i) {
|
|
288
|
+
Sample w = modified ? -_analysisWindow[i] : _analysisWindow[i];
|
|
289
|
+
size_t ti = i + (_fftSamples - _analysisOffset);
|
|
290
|
+
size_t bi = i + offsetPos - _inputLengthSamples;
|
|
291
|
+
timeBuffer[ti] = buffer[bi]*w;
|
|
292
|
+
}
|
|
293
|
+
for (size_t i = _analysisOffset; i < chunk2; ++i) {
|
|
294
|
+
Sample w = _analysisWindow[i];
|
|
295
|
+
size_t ti = i - _analysisOffset;
|
|
296
|
+
size_t bi = offsetPos + i;
|
|
297
|
+
timeBuffer[ti] = buffer[bi]*w;
|
|
298
|
+
}
|
|
299
|
+
for (size_t i = chunk2; i < _blockSamples; ++i) {
|
|
300
|
+
Sample w = _analysisWindow[i];
|
|
301
|
+
size_t ti = i - _analysisOffset;
|
|
302
|
+
size_t bi = i + offsetPos - _inputLengthSamples;
|
|
303
|
+
timeBuffer[ti] = buffer[bi]*w;
|
|
304
|
+
}
|
|
305
|
+
for (size_t i = _blockSamples - _analysisOffset; i < _fftSamples - _analysisOffset; ++i) {
|
|
306
|
+
timeBuffer[i] = 0;
|
|
307
|
+
}
|
|
308
|
+
if (splitComputation) return;
|
|
309
|
+
}
|
|
310
|
+
if (splitComputation) {
|
|
311
|
+
fft.fft(step, timeBuffer.data(), spectrum(channel));
|
|
312
|
+
} else {
|
|
313
|
+
fft.fft(timeBuffer.data(), spectrum(channel));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
void synthesise() {
|
|
318
|
+
for (size_t s = 0; s < synthesiseSteps(); ++s) {
|
|
319
|
+
synthesiseStep(s);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
size_t synthesiseSteps() const {
|
|
323
|
+
return splitComputation ? (_synthesisChannels*(fft.steps() + 1) + 1) : _synthesisChannels;
|
|
324
|
+
}
|
|
325
|
+
void synthesiseStep(size_t step) {
|
|
326
|
+
if (step == 0) { // Extra first step which adds in the effective gain for a pure analysis-synthesis cycle
|
|
327
|
+
addWindowProduct();
|
|
328
|
+
if (splitComputation) return;
|
|
329
|
+
}
|
|
330
|
+
if (splitComputation) --step;
|
|
331
|
+
|
|
332
|
+
size_t fftSteps = splitComputation ? fft.steps() : 0;
|
|
333
|
+
size_t channel = step/(fftSteps + 1);
|
|
334
|
+
step -= channel*(fftSteps + 1);
|
|
335
|
+
|
|
336
|
+
if (splitComputation) {
|
|
337
|
+
if (step < fftSteps) {
|
|
338
|
+
fft.ifft(step, spectrum(channel), timeBuffer.data());
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
fft.ifft(spectrum(channel), timeBuffer.data());
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// extra step after each channel's FFT
|
|
346
|
+
Sample *buffer = output.buffer.data() + channel*_blockSamples;
|
|
347
|
+
size_t outputWrapIndex = _blockSamples - output.pos;
|
|
348
|
+
size_t chunk1 = std::min(_synthesisOffset, outputWrapIndex);
|
|
349
|
+
size_t chunk2 = std::min(_blockSamples, std::max(_synthesisOffset, outputWrapIndex));
|
|
350
|
+
|
|
351
|
+
for (size_t i = 0; i < chunk1; ++i) {
|
|
352
|
+
Sample w = modified ? -_synthesisWindow[i] : _synthesisWindow[i];
|
|
353
|
+
size_t ti = i + (_fftSamples - _synthesisOffset);
|
|
354
|
+
size_t bi = output.pos + i;
|
|
355
|
+
buffer[bi] += timeBuffer[ti]*w;
|
|
356
|
+
}
|
|
357
|
+
for (size_t i = chunk1; i < _synthesisOffset; ++i) {
|
|
358
|
+
Sample w = modified ? -_synthesisWindow[i] : _synthesisWindow[i];
|
|
359
|
+
size_t ti = i + (_fftSamples - _synthesisOffset);
|
|
360
|
+
size_t bi = i + output.pos - _blockSamples;
|
|
361
|
+
buffer[bi] += timeBuffer[ti]*w;
|
|
362
|
+
}
|
|
363
|
+
for (size_t i = _synthesisOffset; i < chunk2; ++i) {
|
|
364
|
+
Sample w = _synthesisWindow[i];
|
|
365
|
+
size_t ti = i - _synthesisOffset;
|
|
366
|
+
size_t bi = output.pos + i;
|
|
367
|
+
buffer[bi] += timeBuffer[ti]*w;
|
|
368
|
+
}
|
|
369
|
+
for (size_t i = chunk2; i < _blockSamples; ++i) {
|
|
370
|
+
Sample w = _synthesisWindow[i];
|
|
371
|
+
size_t ti = i - _synthesisOffset;
|
|
372
|
+
size_t bi = i + output.pos - _blockSamples;
|
|
373
|
+
buffer[bi] += timeBuffer[ti]*w;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
#define COMPAT_SPELLING(name, alt) \
|
|
378
|
+
template<class ...Args> \
|
|
379
|
+
void alt(Args &&...args) { \
|
|
380
|
+
name(std::forward<Args>(args)...); \
|
|
381
|
+
}
|
|
382
|
+
COMPAT_SPELLING(analyse, analyze);
|
|
383
|
+
COMPAT_SPELLING(analyseStep, analyseStep);
|
|
384
|
+
COMPAT_SPELLING(analyseSteps, analyzeSteps);
|
|
385
|
+
COMPAT_SPELLING(synthesise, synthesize);
|
|
386
|
+
COMPAT_SPELLING(synthesiseStep, synthesizeStep);
|
|
387
|
+
COMPAT_SPELLING(synthesiseSteps, synthesizeSteps);
|
|
388
|
+
|
|
389
|
+
/// Input (only available so we can save/restore the input state)
|
|
390
|
+
struct Input {
|
|
391
|
+
void swap(Input &other) {
|
|
392
|
+
std::swap(pos, other.pos);
|
|
393
|
+
std::swap(buffer, other.buffer);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
Input & operator=(const Input &other) {
|
|
397
|
+
pos = other.pos;
|
|
398
|
+
buffer.assign(other.buffer.begin(), other.buffer.end());
|
|
399
|
+
return *this;
|
|
400
|
+
}
|
|
401
|
+
private:
|
|
402
|
+
friend struct DynamicSTFT;
|
|
403
|
+
size_t pos = 0;
|
|
404
|
+
std::vector<Sample> buffer;
|
|
405
|
+
};
|
|
406
|
+
Input input;
|
|
407
|
+
|
|
408
|
+
/// Output (only available so we can save/restore the output state)
|
|
409
|
+
struct Output {
|
|
410
|
+
void swap(Output &other) {
|
|
411
|
+
std::swap(pos, other.pos);
|
|
412
|
+
std::swap(buffer, other.buffer);
|
|
413
|
+
std::swap(windowProducts, other.windowProducts);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
Output & operator=(const Output &other) {
|
|
417
|
+
pos = other.pos;
|
|
418
|
+
buffer.assign(other.buffer.begin(), other.buffer.end());
|
|
419
|
+
windowProducts.assign(other.windowProducts.begin(), other.windowProducts.end());
|
|
420
|
+
return *this;
|
|
421
|
+
}
|
|
422
|
+
private:
|
|
423
|
+
friend struct DynamicSTFT;
|
|
424
|
+
size_t pos = 0;
|
|
425
|
+
std::vector<Sample> buffer;
|
|
426
|
+
std::vector<Sample> windowProducts;
|
|
427
|
+
};
|
|
428
|
+
Output output;
|
|
429
|
+
|
|
430
|
+
private:
|
|
431
|
+
static constexpr Sample almostZero = 1e-30;
|
|
432
|
+
|
|
433
|
+
size_t _analysisChannels, _synthesisChannels, _inputLengthSamples, _blockSamples, _fftSamples, _fftBins;
|
|
434
|
+
size_t _defaultInterval = 0;
|
|
435
|
+
|
|
436
|
+
std::vector<Sample> _analysisWindow, _synthesisWindow;
|
|
437
|
+
size_t _analysisOffset = 0, _synthesisOffset = 0;
|
|
438
|
+
|
|
439
|
+
std::vector<Complex> spectrumBuffer;
|
|
440
|
+
std::vector<Sample> timeBuffer;
|
|
441
|
+
|
|
442
|
+
size_t _samplesSinceSynthesis = 0, _samplesSinceAnalysis = 0;
|
|
443
|
+
|
|
444
|
+
void addWindowProduct() {
|
|
445
|
+
_samplesSinceSynthesis = 0;
|
|
446
|
+
|
|
447
|
+
int windowShift = int(_synthesisOffset) - int(_analysisOffset);
|
|
448
|
+
size_t wMin = std::max<ptrdiff_t>(0, windowShift);
|
|
449
|
+
size_t wMax = std::min<ptrdiff_t>(_blockSamples, int(_blockSamples) + windowShift);
|
|
450
|
+
|
|
451
|
+
Sample *windowProduct = output.windowProducts.data();
|
|
452
|
+
size_t outputWrapIndex = _blockSamples - output.pos;
|
|
453
|
+
size_t chunk1 = std::min<size_t>(wMax, std::max<size_t>(wMin, outputWrapIndex));
|
|
454
|
+
for (size_t i = wMin; i < chunk1; ++i) {
|
|
455
|
+
Sample wa = _analysisWindow[i - windowShift];
|
|
456
|
+
Sample ws = _synthesisWindow[i];
|
|
457
|
+
size_t bi = output.pos + i;
|
|
458
|
+
windowProduct[bi] += wa*ws*_fftSamples;
|
|
459
|
+
}
|
|
460
|
+
for (size_t i = chunk1; i < wMax; ++i) {
|
|
461
|
+
Sample wa = _analysisWindow[i - windowShift];
|
|
462
|
+
Sample ws = _synthesisWindow[i];
|
|
463
|
+
size_t bi = i + output.pos - _blockSamples;
|
|
464
|
+
windowProduct[bi] += wa*ws*_fftSamples;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Copied from DSP library `windows.h`
|
|
469
|
+
class Kaiser {
|
|
470
|
+
inline static double bessel0(double x) {
|
|
471
|
+
const double significanceLimit = 1e-4;
|
|
472
|
+
double result = 0;
|
|
473
|
+
double term = 1;
|
|
474
|
+
double m = 0;
|
|
475
|
+
while (term > significanceLimit) {
|
|
476
|
+
result += term;
|
|
477
|
+
++m;
|
|
478
|
+
term *= (x*x)/(4*m*m);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return result;
|
|
482
|
+
}
|
|
483
|
+
double beta;
|
|
484
|
+
double invB0;
|
|
485
|
+
|
|
486
|
+
static double heuristicBandwidth(double bandwidth) {
|
|
487
|
+
return bandwidth + 8/((bandwidth + 3)*(bandwidth + 3)) + 0.25*std::max(3 - bandwidth, 0.0);
|
|
488
|
+
}
|
|
489
|
+
public:
|
|
490
|
+
Kaiser(double beta) : beta(beta), invB0(1/bessel0(beta)) {}
|
|
491
|
+
|
|
492
|
+
static Kaiser withBandwidth(double bandwidth, bool heuristicOptimal=false) {
|
|
493
|
+
return Kaiser(bandwidthToBeta(bandwidth, heuristicOptimal));
|
|
494
|
+
}
|
|
495
|
+
static double bandwidthToBeta(double bandwidth, bool heuristicOptimal=false) {
|
|
496
|
+
if (heuristicOptimal) { // Heuristic based on numerical search
|
|
497
|
+
bandwidth = heuristicBandwidth(bandwidth);
|
|
498
|
+
}
|
|
499
|
+
bandwidth = std::max(bandwidth, 2.0);
|
|
500
|
+
double alpha = std::sqrt(bandwidth*bandwidth*0.25 - 1);
|
|
501
|
+
return alpha*M_PI;
|
|
502
|
+
}
|
|
503
|
+
|
|
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
|
+
template<typename Data>
|
|
569
|
+
void fill(Data &&data, size_t size) const {
|
|
570
|
+
double invSize = 1.0/size;
|
|
571
|
+
for (size_t i = 0; i < size; ++i) {
|
|
572
|
+
double r = (2*i + 1)*invSize - 1;
|
|
573
|
+
double arg = std::sqrt(1 - r*r);
|
|
574
|
+
data[i] = bessel0(beta*arg)*invB0;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
class ApproximateConfinedGaussian {
|
|
580
|
+
double gaussianFactor;
|
|
581
|
+
|
|
582
|
+
double gaussian(double x) const {
|
|
583
|
+
return std::exp(-x*x*gaussianFactor);
|
|
584
|
+
}
|
|
585
|
+
public:
|
|
586
|
+
static double bandwidthToSigma(double bandwidth) {
|
|
587
|
+
return 0.3/std::sqrt(bandwidth);
|
|
588
|
+
}
|
|
589
|
+
static ApproximateConfinedGaussian withBandwidth(double bandwidth) {
|
|
590
|
+
return ApproximateConfinedGaussian(bandwidthToSigma(bandwidth));
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
ApproximateConfinedGaussian(double sigma) : gaussianFactor(0.0625/(sigma*sigma)) {}
|
|
594
|
+
|
|
595
|
+
/// Fills an arbitrary container
|
|
596
|
+
template<typename Data>
|
|
597
|
+
void fill(Data &&data, size_t size) const {
|
|
598
|
+
double invSize = 1.0/size;
|
|
599
|
+
double offsetScale = gaussian(1)/(gaussian(3) + gaussian(-1));
|
|
600
|
+
double norm = 1/(gaussian(0) - 2*offsetScale*(gaussian(2)));
|
|
601
|
+
for (size_t i = 0; i < size; ++i) {
|
|
602
|
+
double r = (2*i + 1)*invSize - 1;
|
|
603
|
+
data[i] = norm*(gaussian(r) - offsetScale*(gaussian(r - 2) + gaussian(r + 2)));
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
template<typename Data>
|
|
609
|
+
void forcePerfectReconstruction(Data &&data, size_t windowLength, size_t interval) {
|
|
610
|
+
for (size_t i = 0; i < interval; ++i) {
|
|
611
|
+
double sum2 = 0;
|
|
612
|
+
for (size_t index = i; index < windowLength; index += interval) {
|
|
613
|
+
sum2 += data[index]*data[index];
|
|
614
|
+
}
|
|
615
|
+
double factor = 1/std::sqrt(sum2);
|
|
616
|
+
for (size_t index = i; index < windowLength; index += interval) {
|
|
617
|
+
data[index] *= factor;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
}} // namespace
|
|
624
|
+
|
|
625
|
+
#endif // include guard
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
6
6
|
#import <React/RCTCallInvoker.h>
|
|
7
|
-
#import <ReactCommon/RCTTurboModule.h>
|
|
8
7
|
#endif // RCT_NEW_ARCH_ENABLED
|
|
9
8
|
|
|
10
9
|
#include <audioapi/AudioAPIModuleInstaller.h>
|
|
@@ -16,9 +15,9 @@ using namespace facebook::react;
|
|
|
16
15
|
- (void *)runtime;
|
|
17
16
|
@end
|
|
18
17
|
|
|
19
|
-
#if defined(RCT_NEW_ARCH_ENABLED)
|
|
18
|
+
#if defined(RCT_NEW_ARCH_ENABLED)
|
|
20
19
|
// nothing
|
|
21
|
-
#else // defined(RCT_NEW_ARCH_ENABLED)
|
|
20
|
+
#else // defined(RCT_NEW_ARCH_ENABLED)
|
|
22
21
|
@interface RCTBridge (RCTTurboModule)
|
|
23
22
|
- (std::shared_ptr<facebook::react::CallInvoker>)jsCallInvoker;
|
|
24
23
|
- (void)_tryAndHandleError:(dispatch_block_t)block;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <React/RCTBridgeModule.h>
|
|
4
|
+
#import <React/RCTEventEmitter.h>
|
|
5
|
+
|
|
6
|
+
@class AudioEngine;
|
|
7
|
+
@class NotificationManager;
|
|
8
|
+
@class AudioSessionManager;
|
|
9
|
+
@class LockScreenManager;
|
|
10
|
+
|
|
11
|
+
@interface AudioManagerModule : RCTEventEmitter <RCTBridgeModule>
|
|
12
|
+
|
|
13
|
+
@property (nonatomic, strong) AudioEngine *audioEngine;
|
|
14
|
+
@property (nonatomic, strong) NotificationManager *notificationManager;
|
|
15
|
+
@property (nonatomic, strong) AudioSessionManager *audioSessionManager;
|
|
16
|
+
@property (nonatomic, strong) LockScreenManager *lockScreenManager;
|
|
17
|
+
|
|
18
|
+
@end
|