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.
- package/android/build.gradle +2 -0
- package/android/src/main/cpp/{core/AudioAPIInstaller.cpp → AudioAPIModule.cpp} +12 -11
- package/android/src/main/cpp/{core/AudioAPIInstaller.h → AudioAPIModule.h} +9 -11
- package/android/src/main/cpp/OnLoad.cpp +2 -2
- package/android/src/main/cpp/core/AudioDecoder.cpp +5 -5
- package/android/src/main/cpp/core/AudioPlayer.cpp +12 -0
- package/android/src/main/cpp/core/AudioPlayer.h +2 -0
- package/android/src/main/java/com/swmansion/audioapi/{module/AudioAPIInstaller.kt → AudioAPIModule.kt} +22 -10
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +31 -6
- package/android/src/oldarch/NativeAudioAPIModuleSpec.java +37 -0
- package/common/cpp/HostObjects/AudioBufferSourceNodeHostObject.h +1 -2
- package/common/cpp/HostObjects/AudioContextHostObject.h +34 -1
- package/common/cpp/HostObjects/BaseAudioContextHostObject.h +8 -0
- package/common/cpp/HostObjects/GainNodeHostObject.h +2 -2
- package/common/cpp/HostObjects/StretcherNodeHostObject.h +35 -0
- package/common/cpp/core/AnalyserNode.cpp +2 -2
- package/common/cpp/core/AnalyserNode.h +1 -1
- package/common/cpp/core/AudioBuffer.cpp +4 -2
- package/common/cpp/core/AudioBuffer.h +1 -1
- package/common/cpp/core/AudioBufferSourceNode.cpp +3 -3
- package/common/cpp/core/AudioBufferSourceNode.h +3 -3
- package/common/cpp/core/AudioBus.cpp +8 -0
- package/common/cpp/core/AudioBus.h +3 -0
- package/common/cpp/core/AudioContext.cpp +10 -0
- package/common/cpp/core/AudioContext.h +2 -0
- package/common/cpp/core/AudioDecoder.h +2 -1
- package/common/cpp/core/AudioDestinationNode.cpp +1 -1
- package/common/cpp/core/AudioDestinationNode.h +1 -1
- package/common/cpp/core/AudioNode.cpp +10 -6
- package/common/cpp/core/AudioNode.h +5 -3
- package/common/cpp/core/AudioScheduledSourceNode.cpp +1 -1
- package/common/cpp/core/AudioScheduledSourceNode.h +1 -1
- package/common/cpp/core/BaseAudioContext.cpp +7 -0
- package/common/cpp/core/BaseAudioContext.h +2 -0
- package/common/cpp/core/BiquadFilterNode.cpp +1 -1
- package/common/cpp/core/BiquadFilterNode.h +1 -1
- package/common/cpp/core/GainNode.cpp +3 -1
- package/common/cpp/core/GainNode.h +1 -1
- package/common/cpp/core/OscillatorNode.cpp +3 -1
- package/common/cpp/core/OscillatorNode.h +1 -1
- package/common/cpp/core/StereoPannerNode.cpp +1 -1
- package/common/cpp/core/StereoPannerNode.h +1 -1
- package/common/cpp/core/StretcherNode.cpp +96 -0
- package/common/cpp/core/StretcherNode.h +63 -0
- package/common/cpp/installer/AudioAPIModuleInstaller.h +49 -0
- package/common/cpp/libs/dsp/LICENSE.txt +21 -0
- package/common/cpp/libs/dsp/README.md +40 -0
- package/common/cpp/libs/dsp/common.h +47 -0
- package/common/cpp/libs/dsp/curves.h +371 -0
- package/common/cpp/libs/dsp/delay.h +717 -0
- package/common/cpp/libs/dsp/envelopes.h +523 -0
- package/common/cpp/libs/dsp/fft.h +523 -0
- package/common/cpp/libs/dsp/filters.h +436 -0
- package/common/cpp/libs/dsp/mix.h +218 -0
- package/common/cpp/libs/dsp/perf.h +84 -0
- package/common/cpp/libs/dsp/rates.h +184 -0
- package/common/cpp/libs/dsp/spectral.h +496 -0
- package/common/cpp/libs/dsp/windows.h +219 -0
- package/common/cpp/libs/signalsmith-stretch.h +637 -0
- package/common/cpp/types/TimeStretchType.h +6 -0
- package/ios/AudioAPIModule.h +1 -1
- package/ios/AudioAPIModule.mm +10 -3
- package/ios/core/AudioDecoder.mm +2 -3
- package/ios/core/AudioPlayer.h +14 -0
- package/ios/core/AudioPlayer.m +86 -25
- package/ios/core/IOSAudioPlayer.h +2 -0
- package/ios/core/IOSAudioPlayer.mm +10 -0
- package/lib/module/core/AudioContext.js +7 -1
- package/lib/module/core/AudioContext.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +4 -0
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/module/core/StretcherNode.js +12 -0
- package/lib/module/core/StretcherNode.js.map +1 -0
- package/lib/module/index.js +12 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +1 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/specs/NativeAudioAPIModule.js +5 -0
- package/lib/module/specs/NativeAudioAPIModule.js.map +1 -0
- package/lib/module/web-core/AudioContext.js +6 -0
- package/lib/module/web-core/AudioContext.js.map +1 -1
- package/lib/typescript/core/AudioContext.d.ts +2 -0
- package/lib/typescript/core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/core/BaseAudioContext.d.ts +2 -0
- package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/core/StretcherNode.d.ts +10 -0
- package/lib/typescript/core/StretcherNode.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +5 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +1 -1
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +11 -1
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts +7 -0
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -0
- package/lib/typescript/web-core/AudioContext.d.ts +3 -1
- package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
- package/package.json +9 -7
- package/src/core/AudioContext.ts +9 -1
- package/src/core/BaseAudioContext.ts +5 -0
- package/src/core/StretcherNode.ts +15 -0
- package/src/index.ts +17 -3
- package/src/index.web.ts +1 -0
- package/src/interfaces.ts +13 -1
- package/src/specs/NativeAudioAPIModule.ts +7 -0
- package/src/web-core/AudioContext.tsx +9 -1
- package/android/src/main/java/com/swmansion/audioapi/nativemodules/AudioAPIModule.kt +0 -26
- package/common/cpp/HostObjects/AudioAPIInstallerHostObject.h +0 -56
- package/lib/module/specs/global.d.js +0 -4
- package/lib/module/specs/global.d.js.map +0 -1
- package/lib/module/specs/install.js +0 -18
- package/lib/module/specs/install.js.map +0 -1
- package/lib/typescript/specs/install.d.ts +0 -7
- package/lib/typescript/specs/install.d.ts.map +0 -1
- package/src/specs/global.d.ts +0 -12
- package/src/specs/install.ts +0 -32
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
#include "./common.h"
|
|
2
|
+
|
|
3
|
+
#ifndef SIGNALSMITH_DSP_CURVES_H
|
|
4
|
+
#define SIGNALSMITH_DSP_CURVES_H
|
|
5
|
+
|
|
6
|
+
#include <vector>
|
|
7
|
+
#include <algorithm> // std::stable_sort
|
|
8
|
+
|
|
9
|
+
namespace signalsmith {
|
|
10
|
+
namespace curves {
|
|
11
|
+
/** @defgroup Curves Curves
|
|
12
|
+
@brief User-defined mapping functions
|
|
13
|
+
|
|
14
|
+
@{
|
|
15
|
+
@file
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/// Linear map for real values.
|
|
19
|
+
template<typename Sample=double>
|
|
20
|
+
class Linear {
|
|
21
|
+
Sample a1, a0;
|
|
22
|
+
public:
|
|
23
|
+
Linear() : Linear(0, 1) {}
|
|
24
|
+
Linear(Sample a0, Sample a1) : a1(a1), a0(a0) {}
|
|
25
|
+
/// Construct by from/to value pairs
|
|
26
|
+
Linear(Sample x0, Sample x1, Sample y0, Sample y1) : a1((x0 == x1) ? 0 : (y1 - y0)/(x1 - x0)), a0(y0 - x0*a1) {}
|
|
27
|
+
|
|
28
|
+
Sample operator ()(Sample x) const {
|
|
29
|
+
return a0 + x*a1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Sample dx() const {
|
|
33
|
+
return a1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Returns the inverse map (with some numerical error)
|
|
37
|
+
Linear inverse() const {
|
|
38
|
+
Sample invA1 = 1/a1;
|
|
39
|
+
return Linear(-a0*invA1, invA1);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/// A real-valued cubic curve. It has a "start" point where accuracy is highest.
|
|
44
|
+
template<typename Sample=double>
|
|
45
|
+
class Cubic {
|
|
46
|
+
Sample xStart, a0, a1, a2, a3;
|
|
47
|
+
|
|
48
|
+
// Only use with y0 != y1
|
|
49
|
+
static inline Sample gradient(Sample x0, Sample x1, Sample y0, Sample y1) {
|
|
50
|
+
return (y1 - y0)/(x1 - x0);
|
|
51
|
+
}
|
|
52
|
+
// Ensure a gradient produces monotonic segments
|
|
53
|
+
static inline void ensureMonotonic(Sample &curveGrad, Sample gradA, Sample gradB) {
|
|
54
|
+
if ((gradA <= 0 && gradB >= 0) || (gradA >= 0 && gradB <= 0)) {
|
|
55
|
+
curveGrad = 0; // point is a local minimum/maximum
|
|
56
|
+
} else {
|
|
57
|
+
if (std::abs(curveGrad) > std::abs(gradA*3)) {
|
|
58
|
+
curveGrad = gradA*3;
|
|
59
|
+
}
|
|
60
|
+
if (std::abs(curveGrad) > std::abs(gradB*3)) {
|
|
61
|
+
curveGrad = gradB*3;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// When we have duplicate x-values (either side) make up a gradient
|
|
66
|
+
static inline void chooseGradient(Sample &curveGrad, Sample grad1, Sample curveGradOther, Sample y0, Sample y1, bool monotonic) {
|
|
67
|
+
curveGrad = 2*grad1 - curveGradOther;
|
|
68
|
+
if (y0 != y1 && (y1 > y0) != (grad1 >= 0)) { // not duplicate y, but a local min/max
|
|
69
|
+
curveGrad = 0;
|
|
70
|
+
} else if (monotonic) {
|
|
71
|
+
if (grad1 >= 0) {
|
|
72
|
+
curveGrad = std::max<Sample>(0, curveGrad);
|
|
73
|
+
} else {
|
|
74
|
+
curveGrad = std::min<Sample>(0, curveGrad);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
public:
|
|
79
|
+
Cubic() : Cubic(0, 0, 0, 0, 0) {}
|
|
80
|
+
Cubic(Sample xStart, Sample a0, Sample a1, Sample a2, Sample a3) : xStart(xStart), a0(a0), a1(a1), a2(a2), a3(a3) {}
|
|
81
|
+
|
|
82
|
+
Sample operator ()(Sample x) const {
|
|
83
|
+
x -= xStart;
|
|
84
|
+
return a0 + x*(a1 + x*(a2 + x*a3));
|
|
85
|
+
}
|
|
86
|
+
/// The reference x-value, used as the centre of the cubic expansion
|
|
87
|
+
Sample start() const {
|
|
88
|
+
return xStart;
|
|
89
|
+
}
|
|
90
|
+
/// Differentiate
|
|
91
|
+
Cubic dx() const {
|
|
92
|
+
return {xStart, a1, 2*a2, 3*a3, 0};
|
|
93
|
+
}
|
|
94
|
+
Sample dx(Sample x) const {
|
|
95
|
+
x -= xStart;
|
|
96
|
+
return a1 + x*(2*a2 + x*(3*a3));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Cubic segment based on start/end values and gradients
|
|
100
|
+
static Cubic hermite(Sample x0, Sample x1, Sample y0, Sample y1, Sample g0, Sample g1) {
|
|
101
|
+
Sample xScale = 1/(x1 - x0);
|
|
102
|
+
return {
|
|
103
|
+
x0, y0, g0,
|
|
104
|
+
(3*(y1 - y0)*xScale - 2*g0 - g1)*xScale,
|
|
105
|
+
(2*(y0 - y1)*xScale + g0 + g1)*(xScale*xScale)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Cubic segment (valid between `x1` and `x2`), which is smooth when applied to an adjacent set of points.
|
|
110
|
+
If `x0 == x1` or `x2 == x3` it will choose a gradient which continues in a quadratic curve, or 0 if the point is a local minimum/maximum.
|
|
111
|
+
*/
|
|
112
|
+
static Cubic smooth(Sample x0, Sample x1, Sample x2, Sample x3, Sample y0, Sample y1, Sample y2, Sample y3, bool monotonic=false) {
|
|
113
|
+
if (x1 == x2) return {0, y1, 0, 0, 0}; // zero-width segment, just return constant
|
|
114
|
+
|
|
115
|
+
Sample grad1 = gradient(x1, x2, y1, y2);
|
|
116
|
+
Sample curveGrad1 = grad1;
|
|
117
|
+
bool chooseGrad1 = false;
|
|
118
|
+
if (x0 != x1) { // we have a defined x0-x1 gradient
|
|
119
|
+
Sample grad0 = gradient(x0, x1, y0, y1);
|
|
120
|
+
curveGrad1 = (grad0 + grad1)*Sample(0.5);
|
|
121
|
+
if (monotonic) ensureMonotonic(curveGrad1, grad0, grad1);
|
|
122
|
+
} else if (y0 != y1 && (y1 > y0) != (grad1 >= 0)) {
|
|
123
|
+
curveGrad1 = 0; // set to 0 if it's a min/max
|
|
124
|
+
} else {
|
|
125
|
+
curveGrad1 = 0;
|
|
126
|
+
chooseGrad1 = true;
|
|
127
|
+
}
|
|
128
|
+
Sample curveGrad2;
|
|
129
|
+
if (x2 != x3) { // we have a defined x1-x2 gradient
|
|
130
|
+
Sample grad2 = gradient(x2, x3, y2, y3);
|
|
131
|
+
curveGrad2 = (grad1 + grad2)*Sample(0.5);
|
|
132
|
+
if (monotonic) ensureMonotonic(curveGrad2, grad1, grad2);
|
|
133
|
+
} else {
|
|
134
|
+
chooseGradient(curveGrad2, grad1, curveGrad1, y2, y3, monotonic);
|
|
135
|
+
}
|
|
136
|
+
if (chooseGrad1) {
|
|
137
|
+
chooseGradient(curveGrad1, grad1, curveGrad2, y0, y1, monotonic);
|
|
138
|
+
}
|
|
139
|
+
return hermite(x1, x2, y1, y2, curveGrad1, curveGrad2);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/** Smooth interpolation (optionally monotonic) between points, using cubic segments.
|
|
144
|
+
\diagram{cubic-segments-example.svg,Example curve including a repeated point and an instantaneous jump. The curve is flat beyond the first/last points.}
|
|
145
|
+
To produce a sharp corner, use a repeated point. The gradient is flat at the edges, unless you use repeated points at the start/end.*/
|
|
146
|
+
template<typename Sample=double>
|
|
147
|
+
class CubicSegmentCurve {
|
|
148
|
+
struct Point {
|
|
149
|
+
Sample x, y;
|
|
150
|
+
Sample lineGrad = 0, curveGrad = 0;
|
|
151
|
+
bool hasCurveGrad = false;
|
|
152
|
+
|
|
153
|
+
Point() : Point(0, 0) {}
|
|
154
|
+
Point(Sample x, Sample y) : x(x), y(y) {}
|
|
155
|
+
|
|
156
|
+
bool operator <(const Point &other) const {
|
|
157
|
+
return x < other.x;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
std::vector<Point> points;
|
|
161
|
+
Point first{0, 0}, last{0, 0};
|
|
162
|
+
|
|
163
|
+
std::vector<Cubic<Sample>> _segments{1};
|
|
164
|
+
// Not public because it's only valid inside the bounds
|
|
165
|
+
const Cubic<Sample> & findSegment(Sample x) const {
|
|
166
|
+
// Binary search
|
|
167
|
+
size_t low = 0, high = _segments.size();
|
|
168
|
+
while (true) {
|
|
169
|
+
size_t mid = (low + high)/2;
|
|
170
|
+
if (low == mid) break;
|
|
171
|
+
if (_segments[mid].start() <= x) {
|
|
172
|
+
low = mid;
|
|
173
|
+
} else {
|
|
174
|
+
high = mid;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return _segments[low];
|
|
178
|
+
}
|
|
179
|
+
public:
|
|
180
|
+
Sample lowGrad = 0;
|
|
181
|
+
Sample highGrad = 0;
|
|
182
|
+
|
|
183
|
+
/// Clear existing points and segments
|
|
184
|
+
void clear() {
|
|
185
|
+
points.resize(0);
|
|
186
|
+
_segments.resize(0);
|
|
187
|
+
first = last = {0, 0};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// Add a new point, but does not recalculate the segments. `corner` just writes the point twice, for convenience.
|
|
191
|
+
CubicSegmentCurve & add(Sample x, Sample y, bool corner=false) {
|
|
192
|
+
points.push_back({x, y});
|
|
193
|
+
if (corner) points.push_back({x, y});
|
|
194
|
+
return *this;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/// Recalculates the segments.
|
|
198
|
+
void update(bool monotonic=false, bool extendGrad=true, Sample monotonicFactor=3) {
|
|
199
|
+
if (points.empty()) add(0, 0);
|
|
200
|
+
std::stable_sort(points.begin(), points.end()); // Ensure ascending order
|
|
201
|
+
_segments.resize(0);
|
|
202
|
+
|
|
203
|
+
// Calculate the point-to-point gradients
|
|
204
|
+
for (size_t i = 1; i < points.size(); ++i) {
|
|
205
|
+
auto &prev = points[i - 1];
|
|
206
|
+
auto &next = points[i];
|
|
207
|
+
if (prev.x != next.x) {
|
|
208
|
+
prev.lineGrad = (next.y - prev.y)/(next.x - prev.x);
|
|
209
|
+
} else {
|
|
210
|
+
prev.lineGrad = 0;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (auto &p : points) p.hasCurveGrad = false;
|
|
215
|
+
points[0].curveGrad = lowGrad;
|
|
216
|
+
points[0].hasCurveGrad = true;
|
|
217
|
+
points.back().curveGrad = highGrad;
|
|
218
|
+
points.back().hasCurveGrad = true;
|
|
219
|
+
|
|
220
|
+
// Calculate curve gradient where we know it
|
|
221
|
+
for (size_t i = 1; i + 1 < points.size(); ++i) {
|
|
222
|
+
auto &p0 = points[i - 1];
|
|
223
|
+
auto &p1 = points[i];
|
|
224
|
+
auto &p2 = points[i + 1];
|
|
225
|
+
if (p0.x != p1.x && p1.x != p2.x) {
|
|
226
|
+
p1.curveGrad = (p0.lineGrad + p1.lineGrad)*Sample(0.5);
|
|
227
|
+
p1.hasCurveGrad = true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (size_t i = 1; i < points.size(); ++i) {
|
|
232
|
+
Point &p1 = points[i - 1];
|
|
233
|
+
Point &p2 = points[i];
|
|
234
|
+
if (p1.x == p2.x) continue;
|
|
235
|
+
if (p1.hasCurveGrad) {
|
|
236
|
+
if (!p2.hasCurveGrad) {
|
|
237
|
+
p2.curveGrad = 2*p1.lineGrad - p1.curveGrad;
|
|
238
|
+
}
|
|
239
|
+
} else if (p2.hasCurveGrad) {
|
|
240
|
+
p1.curveGrad = 2*p1.lineGrad - p2.curveGrad;
|
|
241
|
+
} else {
|
|
242
|
+
p1.curveGrad = p2.curveGrad = p1.lineGrad;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (monotonic) {
|
|
247
|
+
for (size_t i = 1; i < points.size(); ++i) {
|
|
248
|
+
Point &p1 = points[i - 1];
|
|
249
|
+
Point &p2 = points[i];
|
|
250
|
+
if (p1.x != p2.x) {
|
|
251
|
+
if (p1.lineGrad >= 0) {
|
|
252
|
+
p1.curveGrad = std::max<Sample>(0, std::min(p1.curveGrad, p1.lineGrad*monotonicFactor));
|
|
253
|
+
p2.curveGrad = std::max<Sample>(0, std::min(p2.curveGrad, p1.lineGrad*monotonicFactor));
|
|
254
|
+
} else {
|
|
255
|
+
p1.curveGrad = std::min<Sample>(0, std::max(p1.curveGrad, p1.lineGrad*monotonicFactor));
|
|
256
|
+
p2.curveGrad = std::min<Sample>(0, std::max(p2.curveGrad, p1.lineGrad*monotonicFactor));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
for (size_t i = 1; i < points.size(); ++i) {
|
|
263
|
+
Point &p1 = points[i - 1];
|
|
264
|
+
Point &p2 = points[i];
|
|
265
|
+
if (p1.x != p2.x) {
|
|
266
|
+
_segments.push_back(Segment::hermite(p1.x, p2.x, p1.y, p2.y, p1.curveGrad, p2.curveGrad));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
first = points[0];
|
|
271
|
+
last = points.back();
|
|
272
|
+
if (extendGrad && _segments.size()) {
|
|
273
|
+
if (points[0].x != points[1].x || points[0].y == points[1].y) {
|
|
274
|
+
lowGrad = _segments[0].dx(first.x);
|
|
275
|
+
}
|
|
276
|
+
auto &last = points.back(), &last2 = points[points.size() - 1];
|
|
277
|
+
if (last.x != last2.x || last.y == last2.y) {
|
|
278
|
+
highGrad = _segments.back().dx(last.x);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/// Reads a value out from the curve.
|
|
284
|
+
Sample operator()(Sample x) const {
|
|
285
|
+
if (x <= first.x) return first.y + (x - first.x)*lowGrad;
|
|
286
|
+
if (x >= last.x) return last.y + (x - last.x)*highGrad;
|
|
287
|
+
return findSegment(x)(x);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
CubicSegmentCurve dx() const {
|
|
291
|
+
CubicSegmentCurve result{*this};
|
|
292
|
+
result.first.y = lowGrad;
|
|
293
|
+
result.last.y = highGrad;
|
|
294
|
+
result.lowGrad = result.highGrad = 0;
|
|
295
|
+
for (auto &s : result._segments) {
|
|
296
|
+
s = s.dx();
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
Sample dx(Sample x) const {
|
|
301
|
+
if (x < first.x) return lowGrad;
|
|
302
|
+
if (x >= last.x) return highGrad;
|
|
303
|
+
return findSegment(x).dx(x);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
using Segment = Cubic<Sample>;
|
|
307
|
+
std::vector<Segment> & segments() {
|
|
308
|
+
return _segments;
|
|
309
|
+
}
|
|
310
|
+
const std::vector<Segment> & segments() const {
|
|
311
|
+
return _segments;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
/** A warped-range map, based on 1/x
|
|
316
|
+
\diagram{curves-reciprocal-example.svg}*/
|
|
317
|
+
template<typename Sample=double>
|
|
318
|
+
class Reciprocal {
|
|
319
|
+
Sample a, b, c, d; // (a + bx)/(c + dx)
|
|
320
|
+
Reciprocal(Sample a, Sample b, Sample c, Sample d) : a(a), b(b), c(c), d(d) {}
|
|
321
|
+
public:
|
|
322
|
+
/** Decent approximation to the Bark scale
|
|
323
|
+
|
|
324
|
+
The Bark index goes from 1-24, but this map is valid from approximately 0.25 - 27.5.
|
|
325
|
+
You can get the bandwidth by `barkScale.dx(barkIndex)`.
|
|
326
|
+
\diagram{curves-reciprocal-approx-bark.svg}*/
|
|
327
|
+
static Reciprocal<Sample> barkScale() {
|
|
328
|
+
return {1, 10, 24, 60, 1170, 13500};
|
|
329
|
+
}
|
|
330
|
+
/// Returns a map from 0-1 to the given (non-negative) Hz range.
|
|
331
|
+
static Reciprocal<Sample> barkRange(Sample lowHz, Sample highHz) {
|
|
332
|
+
Reciprocal bark = barkScale();
|
|
333
|
+
Sample lowBark = bark.inverse(lowHz), highBark = bark.inverse(highHz);
|
|
334
|
+
return Reciprocal(lowBark, (lowBark + highBark)/2, highBark).then(bark);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
Reciprocal() : Reciprocal(0, 0.5, 1) {}
|
|
338
|
+
/// If no x-range given, default to the unit range
|
|
339
|
+
Reciprocal(Sample y0, Sample y1, Sample y2) : Reciprocal(0, 0.5, 1, y0, y1, y2) {}
|
|
340
|
+
Reciprocal(Sample x0, Sample x1, Sample x2, Sample y0, Sample y1, Sample y2) {
|
|
341
|
+
Sample kx = (x1 - x0)/(x2 - x1);
|
|
342
|
+
Sample ky = (y1 - y0)/(y2 - y1);
|
|
343
|
+
a = (kx*x2)*y0 - (ky*x0)*y2;
|
|
344
|
+
b = ky*y2 - kx*y0;
|
|
345
|
+
c = kx*x2 - ky*x0;
|
|
346
|
+
d = ky - kx;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
Sample operator ()(double x) const {
|
|
350
|
+
return (a + b*x)/(c + d*x);
|
|
351
|
+
}
|
|
352
|
+
Reciprocal inverse() const {
|
|
353
|
+
return Reciprocal(-a, c, b, -d);
|
|
354
|
+
}
|
|
355
|
+
Sample inverse(Sample y) const {
|
|
356
|
+
return (c*y - a)/(b - d*y);
|
|
357
|
+
}
|
|
358
|
+
Sample dx(Sample x) const {
|
|
359
|
+
Sample l = (c + d*x);
|
|
360
|
+
return (b*c - a*d)/(l*l);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/// Combine two `Reciprocal`s together in sequence
|
|
364
|
+
Reciprocal then(const Reciprocal &other) const {
|
|
365
|
+
return Reciprocal(other.a*c + other.b*a, other.a*d + other.b*b, other.c*c + other.d*a, other.c*d + other.d*b);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/** @} */
|
|
370
|
+
}} // namespace
|
|
371
|
+
#endif // include guard
|