react-native-audio-api 0.4.12-beta.1 → 0.4.12-beta.3
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 +14 -4
- package/android/CMakeLists.txt +9 -48
- package/android/src/main/cpp/audioapi/CMakeLists.txt +44 -0
- package/android/src/main/cpp/{AudioAPIModule.cpp → audioapi/android/AudioAPIModule.cpp} +1 -4
- package/android/src/main/cpp/{AudioAPIModule.h → audioapi/android/AudioAPIModule.h} +2 -1
- package/android/src/main/cpp/{OnLoad.cpp → audioapi/android/OnLoad.cpp} +2 -1
- package/android/src/main/cpp/{core → audioapi/android/core}/AudioDecoder.cpp +5 -5
- package/android/src/main/cpp/{core → audioapi/android/core}/AudioPlayer.cpp +5 -7
- package/android/src/main/cpp/{core → audioapi/android/core}/AudioPlayer.h +1 -0
- package/android/src/main/cpp/{libs → audioapi/android/libs}/pffft.c +2 -2
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +1 -0
- package/common/cpp/{installer → audioapi}/AudioAPIModuleInstaller.h +5 -3
- package/common/cpp/{HostObjects → audioapi/HostObjects}/AnalyserNodeHostObject.h +3 -3
- package/common/cpp/{HostObjects → audioapi/HostObjects}/AudioBufferHostObject.h +3 -3
- package/common/cpp/{HostObjects → audioapi/HostObjects}/AudioBufferSourceNodeHostObject.h +5 -5
- package/common/cpp/{HostObjects → audioapi/HostObjects}/AudioContextHostObject.h +3 -3
- package/common/cpp/{HostObjects → audioapi/HostObjects}/AudioDestinationNodeHostObject.h +3 -3
- package/common/cpp/{HostObjects → audioapi/HostObjects}/AudioNodeHostObject.h +3 -3
- package/common/cpp/{HostObjects → audioapi/HostObjects}/AudioParamHostObject.h +3 -3
- package/common/cpp/{HostObjects → audioapi/HostObjects}/AudioScheduledSourceNodeHostObject.h +3 -3
- package/common/cpp/{HostObjects → audioapi/HostObjects}/BaseAudioContextHostObject.h +14 -15
- package/common/cpp/{HostObjects → audioapi/HostObjects}/BiquadFilterNodeHostObject.h +4 -4
- package/common/cpp/{HostObjects → audioapi/HostObjects}/GainNodeHostObject.h +4 -4
- package/common/cpp/{HostObjects → audioapi/HostObjects}/OscillatorNodeHostObject.h +5 -5
- package/common/cpp/{HostObjects → audioapi/HostObjects}/PeriodicWaveHostObject.h +2 -2
- package/common/cpp/{HostObjects → audioapi/HostObjects}/StereoPannerNodeHostObject.h +4 -4
- package/common/cpp/audioapi/HostObjects/StretcherNodeHostObject.h +35 -0
- package/common/cpp/{core → audioapi/core}/AudioContext.cpp +6 -6
- package/common/cpp/{core → audioapi/core}/AudioContext.h +2 -2
- package/common/cpp/{core → audioapi/core}/AudioNode.cpp +5 -7
- package/common/cpp/{core → audioapi/core}/AudioNode.h +5 -4
- package/common/cpp/{core → audioapi/core}/AudioParam.cpp +3 -4
- package/common/cpp/{core → audioapi/core}/AudioParam.h +3 -3
- package/common/cpp/{core → audioapi/core}/BaseAudioContext.cpp +14 -17
- package/common/cpp/{core → audioapi/core}/BaseAudioContext.h +4 -3
- package/common/cpp/{core → audioapi/core/analysis}/AnalyserNode.cpp +6 -10
- package/common/cpp/{core → audioapi/core/analysis}/AnalyserNode.h +4 -3
- package/common/cpp/{core → audioapi/core/destinations}/AudioDestinationNode.cpp +5 -5
- package/common/cpp/{core → audioapi/core/destinations}/AudioDestinationNode.h +2 -2
- package/common/cpp/{core → audioapi/core/effects}/BiquadFilterNode.cpp +4 -4
- package/common/cpp/{core → audioapi/core/effects}/BiquadFilterNode.h +4 -4
- package/common/cpp/{core → audioapi/core/effects}/GainNode.cpp +4 -4
- package/common/cpp/{core → audioapi/core/effects}/GainNode.h +3 -3
- package/common/cpp/{core → audioapi/core/effects}/PeriodicWave.cpp +4 -2
- package/common/cpp/{core → audioapi/core/effects}/PeriodicWave.h +2 -4
- package/common/cpp/{core → audioapi/core/effects}/StereoPannerNode.cpp +5 -7
- package/common/cpp/{core → audioapi/core/effects}/StereoPannerNode.h +4 -3
- package/common/cpp/{core → audioapi/core/effects}/StretcherNode.cpp +4 -6
- package/common/cpp/audioapi/core/effects/StretcherNode.h +35 -0
- package/common/cpp/{core → audioapi/core/sources}/AudioBuffer.cpp +3 -3
- package/common/cpp/{core → audioapi/core/sources}/AudioBufferSourceNode.cpp +8 -11
- package/common/cpp/{core → audioapi/core/sources}/AudioBufferSourceNode.h +5 -3
- package/common/cpp/{core → audioapi/core/sources}/AudioScheduledSourceNode.cpp +6 -8
- package/common/cpp/{core → audioapi/core/sources}/AudioScheduledSourceNode.h +3 -2
- package/common/cpp/{core → audioapi/core/sources}/OscillatorNode.cpp +4 -4
- package/common/cpp/{core → audioapi/core/sources}/OscillatorNode.h +5 -5
- package/common/cpp/{core → audioapi/core/utils}/AudioArray.cpp +2 -4
- package/common/cpp/{core → audioapi/core/utils}/AudioBus.cpp +4 -7
- package/common/cpp/{core → audioapi/core/utils}/AudioBus.h +2 -2
- package/common/cpp/{core → audioapi/core/utils}/AudioNodeManager.cpp +3 -4
- package/common/cpp/{core → audioapi/core/utils}/ParamChangeEvent.cpp +1 -3
- package/common/cpp/{core → audioapi/core/utils}/ParamChangeEvent.h +3 -1
- package/common/cpp/{utils → audioapi/dsp}/AudioUtils.cpp +1 -1
- package/common/cpp/{utils → audioapi/dsp}/FFTFrame.cpp +1 -10
- package/common/cpp/{utils → audioapi/dsp}/FFTFrame.h +4 -3
- package/common/cpp/{utils → audioapi/dsp}/VectorMath.cpp +2 -5
- package/common/cpp/{utils → audioapi/dsp}/VectorMath.h +2 -0
- package/common/cpp/{jsi → audioapi/jsi}/JsiHostObject.cpp +1 -1
- package/common/cpp/{jsi → audioapi/jsi}/JsiHostObject.h +2 -2
- package/common/cpp/{jsi → audioapi/jsi}/JsiPromise.cpp +2 -6
- package/common/cpp/{jsi → audioapi/jsi}/RuntimeAwareCache.h +2 -3
- package/common/cpp/{jsi → audioapi/jsi}/RuntimeLifecycleMonitor.cpp +1 -5
- package/common/cpp/{jsi → audioapi/jsi}/RuntimeLifecycleMonitor.h +3 -0
- package/common/cpp/{libs → audioapi/libs}/dsp/delay.h +35 -35
- package/common/cpp/{libs → audioapi/libs}/dsp/fft.h +21 -21
- package/common/cpp/{libs → audioapi/libs}/dsp/perf.h +3 -3
- package/common/cpp/{libs → audioapi/libs}/dsp/spectral.h +39 -39
- package/common/cpp/{libs → audioapi/libs}/dsp/windows.h +11 -11
- package/common/cpp/{libs → audioapi/libs}/signalsmith-stretch.h +30 -30
- package/ios/{AudioAPIModule.mm → audioapi/ios/AudioAPIModule.mm} +15 -8
- package/ios/{core → audioapi/ios/core}/AudioDecoder.mm +4 -4
- package/ios/{core → audioapi/ios/core}/AudioPlayer.m +1 -1
- package/ios/{core → audioapi/ios/core}/IOSAudioPlayer.mm +4 -4
- package/package.json +6 -6
- package/common/cpp/HostObjects/StretcherNodeHostObject.h +0 -35
- package/common/cpp/core/StretcherNode.h +0 -63
- package/common/cpp/libs/dsp/LICENSE.txt +0 -21
- package/common/cpp/libs/dsp/README.md +0 -40
- package/common/cpp/libs/dsp/curves.h +0 -371
- package/common/cpp/libs/dsp/envelopes.h +0 -523
- package/common/cpp/libs/dsp/filters.h +0 -436
- package/common/cpp/libs/dsp/mix.h +0 -218
- package/common/cpp/libs/dsp/rates.h +0 -184
- package/common/cpp/types/TimeStretchType.h +0 -6
- /package/android/src/main/cpp/{libs → audioapi/android/libs}/pffft.h +0 -0
- /package/common/cpp/{core → audioapi/core}/Constants.h +0 -0
- /package/common/cpp/{core → audioapi/core/sources}/AudioBuffer.h +0 -0
- /package/common/cpp/{types → audioapi/core/types}/BiquadFilterType.h +0 -0
- /package/common/cpp/{types → audioapi/core/types}/ChannelCountMode.h +0 -0
- /package/common/cpp/{types → audioapi/core/types}/ChannelInterpretation.h +0 -0
- /package/common/cpp/{types → audioapi/core/types}/ContextState.h +0 -0
- /package/common/cpp/{types → audioapi/core/types}/OscillatorType.h +0 -0
- /package/common/cpp/{types → audioapi/core/types}/ParamChangeEventType.h +0 -0
- /package/common/cpp/{core → audioapi/core/utils}/AudioArray.h +0 -0
- /package/common/cpp/{core → audioapi/core/utils}/AudioDecoder.h +0 -0
- /package/common/cpp/{core → audioapi/core/utils}/AudioNodeManager.h +0 -0
- /package/common/cpp/{utils → audioapi/core/utils}/Locker.h +0 -0
- /package/common/cpp/{utils → audioapi/dsp}/AudioUtils.h +0 -0
- /package/common/cpp/{jsi → audioapi/jsi}/JsiPromise.h +0 -0
- /package/common/cpp/{libs → audioapi/libs}/dsp/common.h +0 -0
- /package/common/cpp/{libs → audioapi/libs}/miniaudio.h +0 -0
- /package/ios/{AudioAPIModule.h → audioapi/ios/AudioAPIModule.h} +0 -0
- /package/ios/{core → audioapi/ios/core}/AudioPlayer.h +0 -0
- /package/ios/{core → audioapi/ios/core}/IOSAudioPlayer.h +0 -0
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#include <memory>
|
|
4
|
-
#include <string>
|
|
5
|
-
|
|
6
|
-
#include "signalsmith-stretch.h"
|
|
7
|
-
#include "TimeStretchType.h"
|
|
8
|
-
#include "AudioNode.h"
|
|
9
|
-
#include "AudioParam.h"
|
|
10
|
-
|
|
11
|
-
namespace audioapi {
|
|
12
|
-
class AudioBus;
|
|
13
|
-
|
|
14
|
-
class StretcherNode : public AudioNode {
|
|
15
|
-
public:
|
|
16
|
-
explicit StretcherNode(BaseAudioContext *context);
|
|
17
|
-
|
|
18
|
-
[[nodiscard]] std::shared_ptr<AudioParam> getRateParam() const;
|
|
19
|
-
[[nodiscard]] std::shared_ptr<AudioParam> getSemitonesParam() const;
|
|
20
|
-
|
|
21
|
-
protected:
|
|
22
|
-
void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
|
|
23
|
-
std::shared_ptr<AudioBus> processAudio(std::shared_ptr<AudioBus> outputBus, int framesToProcess, bool checkIsAlreadyProcessed) override;
|
|
24
|
-
|
|
25
|
-
private:
|
|
26
|
-
// k-rate params
|
|
27
|
-
std::shared_ptr<AudioParam> rate_;
|
|
28
|
-
std::shared_ptr<AudioParam> semitones_;
|
|
29
|
-
|
|
30
|
-
std::shared_ptr<signalsmith::stretch::SignalsmithStretch<float>> stretch_;
|
|
31
|
-
std::shared_ptr<AudioBus> playbackRateBus_;
|
|
32
|
-
int framesNeededToStretch_ = RENDER_QUANTUM_SIZE;
|
|
33
|
-
|
|
34
|
-
static TimeStretchType fromString(const std::string &type) {
|
|
35
|
-
std::string lowerType = type;
|
|
36
|
-
std::transform(
|
|
37
|
-
lowerType.begin(), lowerType.end(), lowerType.begin(), ::tolower);
|
|
38
|
-
|
|
39
|
-
if (lowerType == "linear")
|
|
40
|
-
return TimeStretchType::LINEAR;
|
|
41
|
-
if (lowerType == "speech")
|
|
42
|
-
return TimeStretchType::SPEECH;
|
|
43
|
-
if (lowerType == "music")
|
|
44
|
-
return TimeStretchType::MUSIC;
|
|
45
|
-
|
|
46
|
-
throw std::invalid_argument("Unknown time stretch type: " + type);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
static std::string toString(TimeStretchType type) {
|
|
50
|
-
switch (type) {
|
|
51
|
-
case TimeStretchType::LINEAR:
|
|
52
|
-
return "linear";
|
|
53
|
-
case TimeStretchType::SPEECH:
|
|
54
|
-
return "speech";
|
|
55
|
-
case TimeStretchType::MUSIC:
|
|
56
|
-
return "music";
|
|
57
|
-
default:
|
|
58
|
-
throw std::invalid_argument("Unknown time stretch type");
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
} // namespace audioapi
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2021 Geraint Luff / Signalsmith Audio Ltd.
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Signalsmith Audio's DSP Library
|
|
2
|
-
|
|
3
|
-
A C++11 header-only library, providing classes/templates for (mostly audio) signal-processing tasks.
|
|
4
|
-
|
|
5
|
-
More detail is in the [main project page](https://signalsmith-audio.co.uk/code/dsp/), and the [Doxygen docs](https://signalsmith-audio.co.uk/code/dsp/html/modules.html).
|
|
6
|
-
|
|
7
|
-
## Basic use
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
git clone https://signalsmith-audio.co.uk/code/dsp.git
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Just include the header file(s) you need, and start using classes:
|
|
14
|
-
|
|
15
|
-
```cpp
|
|
16
|
-
#include "dsp/delay.h"
|
|
17
|
-
|
|
18
|
-
using Delay = signalsmith::delay::Delay<float>;
|
|
19
|
-
Delay delayLine(1024);
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
You can add a compile-time version-check to make sure you have a compatible version of the library:
|
|
23
|
-
```cpp
|
|
24
|
-
#include "dsp/envelopes.h"
|
|
25
|
-
SIGNALSMITH_DSP_VERSION_CHECK(1, 6, 1)
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### Development / contributing
|
|
29
|
-
|
|
30
|
-
Tests (and source-scripts for the above docs) are available in a separate repo:
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
git clone https://signalsmith-audio.co.uk/code/dsp-doc.git
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
The goal (where possible) is to measure/test the actual audio characteristics of the tools (e.g. frequency responses and aliasing levels).
|
|
37
|
-
|
|
38
|
-
### License
|
|
39
|
-
|
|
40
|
-
This code is [MIT licensed](LICENSE.txt). If you'd prefer something else, get in touch.
|
|
@@ -1,371 +0,0 @@
|
|
|
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
|