react-native-audio-api 0.4.11 → 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 (55) hide show
  1. package/common/cpp/HostObjects/AudioBufferSourceNodeHostObject.h +1 -2
  2. package/common/cpp/HostObjects/BaseAudioContextHostObject.h +8 -0
  3. package/common/cpp/HostObjects/GainNodeHostObject.h +2 -2
  4. package/common/cpp/HostObjects/StretcherNodeHostObject.h +35 -0
  5. package/common/cpp/core/AudioBus.cpp +8 -0
  6. package/common/cpp/core/AudioBus.h +3 -0
  7. package/common/cpp/core/AudioDestinationNode.cpp +1 -1
  8. package/common/cpp/core/AudioNode.cpp +9 -5
  9. package/common/cpp/core/AudioNode.h +4 -2
  10. package/common/cpp/core/BaseAudioContext.cpp +7 -0
  11. package/common/cpp/core/BaseAudioContext.h +2 -0
  12. package/common/cpp/core/StretcherNode.cpp +96 -0
  13. package/common/cpp/core/StretcherNode.h +63 -0
  14. package/common/cpp/installer/AudioAPIModuleInstaller.h +4 -4
  15. package/common/cpp/libs/dsp/LICENSE.txt +21 -0
  16. package/common/cpp/libs/dsp/README.md +40 -0
  17. package/common/cpp/libs/dsp/common.h +47 -0
  18. package/common/cpp/libs/dsp/curves.h +371 -0
  19. package/common/cpp/libs/dsp/delay.h +717 -0
  20. package/common/cpp/libs/dsp/envelopes.h +523 -0
  21. package/common/cpp/libs/dsp/fft.h +523 -0
  22. package/common/cpp/libs/dsp/filters.h +436 -0
  23. package/common/cpp/libs/dsp/mix.h +218 -0
  24. package/common/cpp/libs/dsp/perf.h +84 -0
  25. package/common/cpp/libs/dsp/rates.h +184 -0
  26. package/common/cpp/libs/dsp/spectral.h +496 -0
  27. package/common/cpp/libs/dsp/windows.h +219 -0
  28. package/common/cpp/libs/signalsmith-stretch.h +637 -0
  29. package/common/cpp/types/TimeStretchType.h +6 -0
  30. package/ios/core/AudioPlayer.m +2 -2
  31. package/lib/module/core/BaseAudioContext.js +4 -0
  32. package/lib/module/core/BaseAudioContext.js.map +1 -1
  33. package/lib/module/core/StretcherNode.js +12 -0
  34. package/lib/module/core/StretcherNode.js.map +1 -0
  35. package/lib/module/index.js +1 -0
  36. package/lib/module/index.js.map +1 -1
  37. package/lib/module/index.web.js +1 -1
  38. package/lib/module/index.web.js.map +1 -1
  39. package/lib/typescript/core/BaseAudioContext.d.ts +2 -0
  40. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  41. package/lib/typescript/core/StretcherNode.d.ts +10 -0
  42. package/lib/typescript/core/StretcherNode.d.ts.map +1 -0
  43. package/lib/typescript/index.d.ts +1 -0
  44. package/lib/typescript/index.d.ts.map +1 -1
  45. package/lib/typescript/index.web.d.ts +1 -1
  46. package/lib/typescript/index.web.d.ts.map +1 -1
  47. package/lib/typescript/interfaces.d.ts +5 -0
  48. package/lib/typescript/interfaces.d.ts.map +1 -1
  49. package/package.json +1 -1
  50. package/src/core/BaseAudioContext.ts +5 -0
  51. package/src/core/StretcherNode.ts +15 -0
  52. package/src/index.ts +1 -0
  53. package/src/index.web.ts +1 -0
  54. package/src/interfaces.ts +6 -0
  55. package/common/cpp/installer/AudioAPIModuleInstaller.cpp +0 -49
@@ -0,0 +1,84 @@
1
+ #include "./common.h"
2
+
3
+ #ifndef SIGNALSMITH_DSP_PERF_H
4
+ #define SIGNALSMITH_DSP_PERF_H
5
+
6
+ #include <complex>
7
+
8
+ #if defined(__SSE__) || defined(_M_X64)
9
+ # include <xmmintrin.h>
10
+ #else
11
+ # include <cstdint> // for uintptr_t
12
+ #endif
13
+
14
+ namespace signalsmith {
15
+ namespace perf {
16
+ /** @defgroup Performance Performance helpers
17
+ @brief Nothing serious, just some `#defines` and helpers
18
+
19
+ @{
20
+ @file
21
+ */
22
+
23
+ /// *Really* insist that a function/method is inlined (mostly for performance in DEBUG builds)
24
+ #ifndef SIGNALSMITH_INLINE
25
+ #ifdef __GNUC__
26
+ #define SIGNALSMITH_INLINE __attribute__((always_inline)) inline
27
+ #elif defined(__MSVC__)
28
+ #define SIGNALSMITH_INLINE __forceinline inline
29
+ #else
30
+ #define SIGNALSMITH_INLINE inline
31
+ #endif
32
+ #endif
33
+
34
+ /** @brief Complex-multiplication (with optional conjugate second-arg), without handling NaN/Infinity
35
+ The `std::complex` multiplication has edge-cases around NaNs which slow things down and prevent auto-vectorisation. Flags like `-ffast-math` sort this out anyway, but this helps with Debug builds.
36
+ */
37
+ template <bool conjugateSecond=false, typename V>
38
+ SIGNALSMITH_INLINE static std::complex<V> mul(const std::complex<V> &a, const std::complex<V> &b) {
39
+ return conjugateSecond ? std::complex<V>{
40
+ b.real()*a.real() + b.imag()*a.imag(),
41
+ b.real()*a.imag() - b.imag()*a.real()
42
+ } : std::complex<V>{
43
+ a.real()*b.real() - a.imag()*b.imag(),
44
+ a.real()*b.imag() + a.imag()*b.real()
45
+ };
46
+ }
47
+
48
+ #if defined(__SSE__) || defined(_M_X64)
49
+ class StopDenormals {
50
+ unsigned int controlStatusRegister;
51
+ public:
52
+ StopDenormals() : controlStatusRegister(_mm_getcsr()) {
53
+ _mm_setcsr(controlStatusRegister|0x8040); // Flush-to-Zero and Denormals-Are-Zero
54
+ }
55
+ ~StopDenormals() {
56
+ _mm_setcsr(controlStatusRegister);
57
+ }
58
+ };
59
+ #elif (defined (__ARM_NEON) || defined (__ARM_NEON__))
60
+ class StopDenormals {
61
+ uintptr_t status;
62
+ public:
63
+ StopDenormals() {
64
+ uintptr_t asmStatus;
65
+ asm volatile("mrs %0, fpcr" : "=r"(asmStatus));
66
+ status = asmStatus = asmStatus|0x01000000U; // Flush to Zero
67
+ asm volatile("msr fpcr, %0" : : "ri"(asmStatus));
68
+ }
69
+ ~StopDenormals() {
70
+ uintptr_t asmStatus = status;
71
+ asm volatile("msr fpcr, %0" : : "ri"(asmStatus));
72
+ }
73
+ };
74
+ #else
75
+ # if __cplusplus >= 202302L
76
+ # warning "The `StopDenormals` class doesn't do anything for this architecture"
77
+ # endif
78
+ class StopDenormals {}; // FIXME: add for other architectures
79
+ #endif
80
+
81
+ /** @} */
82
+ }} // signalsmith::perf::
83
+
84
+ #endif // include guard
@@ -0,0 +1,184 @@
1
+ #include "./common.h"
2
+
3
+ #ifndef SIGNALSMITH_DSP_RATES_H
4
+ #define SIGNALSMITH_DSP_RATES_H
5
+
6
+ #include "./windows.h"
7
+ #include "./delay.h"
8
+
9
+ namespace signalsmith {
10
+ namespace rates {
11
+ /** @defgroup Rates Multi-rate processing
12
+ @brief Classes for oversampling/upsampling/downsampling etc.
13
+
14
+ @{
15
+ @file
16
+ */
17
+
18
+ /// @brief Fills a container with a Kaiser-windowed sinc for an FIR lowpass.
19
+ /// \diagram{rates-kaiser-sinc.svg,33-point results for various pass/stop frequencies}
20
+ template<class Data>
21
+ void fillKaiserSinc(Data &&data, int length, double passFreq, double stopFreq) {
22
+ if (length <= 0) return;
23
+ double kaiserBandwidth = (stopFreq - passFreq)*length;
24
+ kaiserBandwidth += 1.25/kaiserBandwidth; // heuristic for transition band, see `InterpolatorKaiserSincN`
25
+ auto kaiser = signalsmith::windows::Kaiser::withBandwidth(kaiserBandwidth);
26
+ kaiser.fill(data, length);
27
+
28
+ double centreIndex = (length - 1)*0.5;
29
+ double sincScale = M_PI*(passFreq + stopFreq);
30
+ double ampScale = (passFreq + stopFreq);
31
+ for (int i = 0; i < length; ++i) {
32
+ double x = (i - centreIndex), px = x*sincScale;
33
+ double sinc = (std::abs(px) > 1e-6) ? std::sin(px)*ampScale/px : ampScale;
34
+ data[i] *= sinc;
35
+ }
36
+ };
37
+ /// @brief If only the centre frequency is specified, a heuristic is used to balance ripples and transition width
38
+ /// \diagram{rates-kaiser-sinc-heuristic.svg,The transition width is set to: 0.9/sqrt(length)}
39
+ template<class Data>
40
+ void fillKaiserSinc(Data &&data, int length, double centreFreq) {
41
+ double halfWidth = 0.45/std::sqrt(length);
42
+ if (halfWidth > centreFreq) halfWidth = (halfWidth + centreFreq)*0.5;
43
+ fillKaiserSinc(data, length, centreFreq - halfWidth, centreFreq + halfWidth);
44
+ }
45
+
46
+ /** 2x FIR oversampling for block-based processing.
47
+
48
+ \diagram{rates-oversampler2xfir-responses-45.svg,Upsample response for various lengths}
49
+
50
+ The oversampled signal is stored inside this object, with channels accessed via `oversampler[c]`. For example, you might do:
51
+ \code{.cpp}
52
+ // Upsample from multi-channel input (inputBuffers[c][i] is a sample)
53
+ oversampler.up(inputBuffers, bufferLength)
54
+
55
+ // Modify the contents at the higher rate
56
+ for (int c = 0; c < 2; ++c) {
57
+ float *channel = oversampler[c];
58
+ for (int i = 0; i < bufferLength*2; ++i) {
59
+ channel[i] = std::abs(channel[i]);
60
+ }
61
+ }
62
+
63
+ // Downsample into the multi-channel output
64
+ oversampler.down(outputBuffers, bufferLength);
65
+ \endcode
66
+
67
+ The performance depends not just on the length, but also on where you end the passband, allowing a wider/narrower transition band. Frequencies above this limit (relative to the lower sample-rate) may alias when upsampling and downsampling.
68
+
69
+ \diagram{rates-oversampler2xfir-lengths.svg,Resample error rates for different passband thresholds}
70
+
71
+ Since both upsample and downsample are stateful, channels are meaningful. If your input channel-count doesn't match your output, you can size it to the larger of the two, and use `.upChannel()` and `.downChannel()` to only process the channels which exist.*/
72
+ template<typename Sample>
73
+ struct Oversampler2xFIR {
74
+ Oversampler2xFIR() : Oversampler2xFIR(0, 0) {}
75
+ Oversampler2xFIR(int channels, int maxBlock, int halfLatency=16, double passFreq=0.43) {
76
+ resize(channels, maxBlock, halfLatency, passFreq);
77
+ }
78
+
79
+ void resize(int nChannels, int maxBlockLength) {
80
+ resize(nChannels, maxBlockLength, oneWayLatency);
81
+ }
82
+ void resize(int nChannels, int maxBlockLength, int halfLatency, double passFreq=0.43) {
83
+ oneWayLatency = halfLatency;
84
+ kernelLength = oneWayLatency*2;
85
+ channels = nChannels;
86
+ halfSampleKernel.resize(kernelLength);
87
+ fillKaiserSinc(halfSampleKernel, kernelLength, passFreq, 1 - passFreq);
88
+ inputStride = kernelLength + maxBlockLength;
89
+ inputBuffer.resize(channels*inputStride);
90
+ stride = (maxBlockLength + kernelLength)*2;
91
+ buffer.resize(stride*channels);
92
+ }
93
+
94
+ void reset() {
95
+ inputBuffer.assign(inputBuffer.size(), 0);
96
+ buffer.assign(buffer.size(), 0);
97
+ }
98
+
99
+ /// @brief Round-trip latency (or equivalently: upsample latency at the higher rate).
100
+ /// This will be twice the value passed into the constructor or `.resize()`.
101
+ int latency() const {
102
+ return kernelLength;
103
+ }
104
+
105
+ /// Upsamples from a multi-channel input into the internal buffer
106
+ template<class Data>
107
+ void up(Data &&data, int lowSamples) {
108
+ for (int c = 0; c < channels; ++c) {
109
+ upChannel(c, data[c], lowSamples);
110
+ }
111
+ }
112
+
113
+ /// Upsamples a single-channel input into the internal buffer
114
+ template<class Data>
115
+ void upChannel(int c, Data &&data, int lowSamples) {
116
+ Sample *inputChannel = inputBuffer.data() + c*inputStride;
117
+ for (int i = 0; i < lowSamples; ++i) {
118
+ inputChannel[kernelLength + i] = data[i];
119
+ }
120
+ Sample *output = (*this)[c];
121
+ for (int i = 0; i < lowSamples; ++i) {
122
+ output[2*i] = inputChannel[i + oneWayLatency];
123
+ Sample *offsetInput = inputChannel + (i + 1);
124
+ Sample sum = 0;
125
+ for (int o = 0; o < kernelLength; ++o) {
126
+ sum += offsetInput[o]*halfSampleKernel[o];
127
+ }
128
+ output[2*i + 1] = sum;
129
+ }
130
+ // Copy the end of the buffer back to the beginning
131
+ for (int i = 0; i < kernelLength; ++i) {
132
+ inputChannel[i] = inputChannel[lowSamples + i];
133
+ }
134
+ }
135
+
136
+ /// Downsamples from the internal buffer to a multi-channel output
137
+ template<class Data>
138
+ void down(Data &&data, int lowSamples) {
139
+ for (int c = 0; c < channels; ++c) {
140
+ downChannel(c, data[c], lowSamples);
141
+ }
142
+ }
143
+
144
+ /// Downsamples a single channel from the internal buffer to a single-channel output
145
+ template<class Data>
146
+ void downChannel(int c, Data &&data, int lowSamples) {
147
+ Sample *input = buffer.data() + c*stride; // no offset for latency
148
+ for (int i = 0; i < lowSamples; ++i) {
149
+ Sample v1 = input[2*i + kernelLength];
150
+ Sample sum = 0;
151
+ for (int o = 0; o < kernelLength; ++o) {
152
+ Sample v2 = input[2*(i + o) + 1];
153
+ sum += v2*halfSampleKernel[o];
154
+ }
155
+ Sample v2 = sum;
156
+ Sample v = (v1 + v2)*Sample(0.5);
157
+ data[i] = v;
158
+ }
159
+ // Copy the end of the buffer back to the beginning
160
+ for (int i = 0; i < kernelLength*2; ++i) {
161
+ input[i] = input[lowSamples*2 + i];
162
+ }
163
+ }
164
+
165
+ /// Gets the samples for a single (higher-rate) channel. The valid length depends how many input samples were passed into `.up()`/`.upChannel()`.
166
+ Sample * operator[](int c) {
167
+ return buffer.data() + kernelLength*2 + stride*c;
168
+ }
169
+ const Sample * operator[](int c) const {
170
+ return buffer.data() + kernelLength*2 + stride*c;
171
+ }
172
+
173
+ private:
174
+ int oneWayLatency, kernelLength;
175
+ int channels;
176
+ int stride, inputStride;
177
+ std::vector<Sample> inputBuffer;
178
+ std::vector<Sample> halfSampleKernel;
179
+ std::vector<Sample> buffer;
180
+ };
181
+
182
+ /** @} */
183
+ }} // namespace
184
+ #endif // include guard