react-native-audio-api 0.3.2 → 0.4.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/README.md +17 -11
- package/android/src/main/cpp/core/AudioPlayer.cpp +38 -11
- package/android/src/main/cpp/core/AudioPlayer.h +5 -1
- package/common/cpp/HostObjects/AnalyserNodeHostObject.h +151 -0
- package/common/cpp/HostObjects/AudioAPIInstallerHostObject.h +11 -3
- package/common/cpp/HostObjects/AudioNodeHostObject.h +5 -0
- package/common/cpp/HostObjects/BaseAudioContextHostObject.h +8 -0
- package/common/cpp/core/AnalyserNode.cpp +237 -0
- package/common/cpp/core/AnalyserNode.h +54 -0
- package/common/cpp/core/AudioContext.cpp +41 -1
- package/common/cpp/core/AudioContext.h +17 -0
- package/common/cpp/core/AudioNode.cpp +21 -8
- package/common/cpp/core/AudioNode.h +1 -0
- package/common/cpp/core/AudioScheduledSourceNode.cpp +1 -2
- package/common/cpp/core/BaseAudioContext.cpp +5 -40
- package/common/cpp/core/BaseAudioContext.h +7 -18
- package/common/cpp/core/Constants.h +11 -2
- package/common/cpp/core/PeriodicWave.cpp +2 -2
- package/common/cpp/jsi/JsiHostObject.h +1 -1
- package/common/cpp/jsi/JsiPromise.h +1 -0
- package/common/cpp/utils/AudioUtils.cpp +7 -0
- package/common/cpp/utils/AudioUtils.h +5 -1
- package/common/cpp/utils/FFTFrame.cpp +69 -23
- package/common/cpp/utils/FFTFrame.h +25 -10
- package/common/cpp/utils/VectorMath.cpp +10 -0
- package/common/cpp/utils/VectorMath.h +2 -0
- package/ios/core/AudioPlayer.h +3 -2
- package/ios/core/AudioPlayer.m +49 -15
- package/ios/core/IOSAudioPlayer.h +3 -1
- package/ios/core/IOSAudioPlayer.mm +46 -10
- package/lib/module/core/AnalyserNode.js +59 -0
- package/lib/module/core/AnalyserNode.js.map +1 -0
- package/lib/module/core/AudioContext.js +2 -2
- package/lib/module/core/AudioContext.js.map +1 -1
- package/lib/module/core/AudioNode.js +5 -5
- package/lib/module/core/AudioNode.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +8 -0
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/module/index.js +35 -6
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.native.js +1 -0
- package/lib/module/index.native.js.map +1 -1
- package/lib/typescript/core/AnalyserNode.d.ts +18 -0
- package/lib/typescript/core/AnalyserNode.d.ts.map +1 -0
- package/lib/typescript/core/AudioContext.d.ts +1 -1
- package/lib/typescript/core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/core/AudioNode.d.ts +2 -2
- package/lib/typescript/core/AudioNode.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/index.d.ts +16 -3
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.native.d.ts +1 -0
- package/lib/typescript/index.native.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +13 -1
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/core/AnalyserNode.ts +85 -0
- package/src/core/AudioContext.ts +2 -2
- package/src/core/AudioNode.ts +5 -5
- package/src/core/BaseAudioContext.ts +10 -0
- package/src/index.native.ts +1 -0
- package/src/index.ts +57 -6
- package/src/interfaces.ts +15 -1
- package/src/specs/global.d.ts +1 -1
|
@@ -5,16 +5,56 @@
|
|
|
5
5
|
#endif
|
|
6
6
|
|
|
7
7
|
#include "AudioContext.h"
|
|
8
|
+
#include "AudioDecoder.h"
|
|
9
|
+
#include "AudioDestinationNode.h"
|
|
8
10
|
|
|
9
11
|
namespace audioapi {
|
|
10
|
-
|
|
11
12
|
AudioContext::AudioContext() : BaseAudioContext() {
|
|
13
|
+
#ifdef ANDROID
|
|
14
|
+
audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio());
|
|
15
|
+
#else
|
|
16
|
+
audioPlayer_ = std::make_shared<IOSAudioPlayer>(this->renderAudio());
|
|
17
|
+
#endif
|
|
18
|
+
sampleRate_ = audioPlayer_->getSampleRate();
|
|
19
|
+
audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate_);
|
|
20
|
+
|
|
21
|
+
audioPlayer_->start();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
AudioContext::AudioContext(int sampleRate) : BaseAudioContext() {
|
|
25
|
+
#ifdef ANDROID
|
|
26
|
+
audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio(), sampleRate);
|
|
27
|
+
#else
|
|
28
|
+
audioPlayer_ =
|
|
29
|
+
std::make_shared<IOSAudioPlayer>(this->renderAudio(), sampleRate);
|
|
30
|
+
#endif
|
|
31
|
+
sampleRate_ = audioPlayer_->getSampleRate();
|
|
32
|
+
audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate_);
|
|
33
|
+
|
|
12
34
|
audioPlayer_->start();
|
|
13
35
|
}
|
|
14
36
|
|
|
37
|
+
AudioContext::~AudioContext() {
|
|
38
|
+
if (isRunning()) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
close();
|
|
43
|
+
}
|
|
44
|
+
|
|
15
45
|
void AudioContext::close() {
|
|
16
46
|
state_ = ContextState::CLOSED;
|
|
17
47
|
audioPlayer_->stop();
|
|
18
48
|
}
|
|
19
49
|
|
|
50
|
+
std::function<void(AudioBus *, int)> AudioContext::renderAudio() {
|
|
51
|
+
if (!isRunning()) {
|
|
52
|
+
return [](AudioBus *, int) {};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return [this](AudioBus *data, int frames) {
|
|
56
|
+
destination_->renderAudio(data, frames);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
20
60
|
} // namespace audioapi
|
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <memory>
|
|
4
|
+
#include <functional>
|
|
4
5
|
|
|
5
6
|
#include "BaseAudioContext.h"
|
|
6
7
|
|
|
7
8
|
namespace audioapi {
|
|
9
|
+
#ifdef ANDROID
|
|
10
|
+
class AudioPlayer;
|
|
11
|
+
#else
|
|
12
|
+
class IOSAudioPlayer;
|
|
13
|
+
#endif
|
|
8
14
|
|
|
9
15
|
class AudioContext : public BaseAudioContext {
|
|
10
16
|
public:
|
|
11
17
|
AudioContext();
|
|
18
|
+
explicit AudioContext(int sampleRate);
|
|
19
|
+
~AudioContext() override;
|
|
12
20
|
|
|
13
21
|
void close();
|
|
22
|
+
|
|
23
|
+
std::function<void(AudioBus *, int)> renderAudio();
|
|
24
|
+
|
|
25
|
+
private:
|
|
26
|
+
#ifdef ANDROID
|
|
27
|
+
std::shared_ptr<AudioPlayer> audioPlayer_;
|
|
28
|
+
#else
|
|
29
|
+
std::shared_ptr<IOSAudioPlayer> audioPlayer_;
|
|
30
|
+
#endif
|
|
14
31
|
};
|
|
15
32
|
|
|
16
33
|
} // namespace audioapi
|
|
@@ -9,9 +9,7 @@ namespace audioapi {
|
|
|
9
9
|
|
|
10
10
|
AudioNode::AudioNode(BaseAudioContext *context) : context_(context) {
|
|
11
11
|
audioBus_ = std::make_shared<AudioBus>(
|
|
12
|
-
context->getSampleRate(),
|
|
13
|
-
context->getBufferSizeInFrames(),
|
|
14
|
-
channelCount_);
|
|
12
|
+
context->getSampleRate(), RENDER_QUANTUM_SIZE, channelCount_);
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
AudioNode::~AudioNode() {
|
|
@@ -49,6 +47,12 @@ void AudioNode::connectNode(const std::shared_ptr<AudioNode> &node) {
|
|
|
49
47
|
node->onInputConnected(this);
|
|
50
48
|
}
|
|
51
49
|
|
|
50
|
+
void AudioNode::disconnect() {
|
|
51
|
+
for (auto &outputNode : outputNodes_) {
|
|
52
|
+
disconnectNode(outputNode);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
52
56
|
void AudioNode::disconnect(const std::shared_ptr<AudioNode> &node) {
|
|
53
57
|
context_->getNodeManager()->addPendingConnection(
|
|
54
58
|
shared_from_this(), node, AudioNodeManager::ConnectionType::DISCONNECT);
|
|
@@ -126,7 +130,7 @@ AudioBus *AudioNode::processAudio(AudioBus *outputBus, int framesToProcess) {
|
|
|
126
130
|
// - it has more than one output, so each output node can get the processed
|
|
127
131
|
// data without re-calculating the node.
|
|
128
132
|
bool canUseOutputBus =
|
|
129
|
-
outputBus !=
|
|
133
|
+
outputBus != nullptr && inputNodes_.size() < 2 && outputNodes_.size() < 2;
|
|
130
134
|
|
|
131
135
|
if (isAlreadyProcessed) {
|
|
132
136
|
// If it was already processed in the rendering quantum, return it.
|
|
@@ -144,8 +148,9 @@ AudioBus *AudioNode::processAudio(AudioBus *outputBus, int framesToProcess) {
|
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
if (inputNodes_.empty()) {
|
|
147
|
-
// If there are no connected inputs,
|
|
148
|
-
// audio
|
|
151
|
+
// If there are no connected inputs, if processing node is the source node,
|
|
152
|
+
// it will fill processing bus with the audio data, otherwise it will return
|
|
153
|
+
// silence.
|
|
149
154
|
processNode(processingBus, framesToProcess);
|
|
150
155
|
return processingBus;
|
|
151
156
|
}
|
|
@@ -200,6 +205,10 @@ void AudioNode::onInputDisabled() {
|
|
|
200
205
|
}
|
|
201
206
|
|
|
202
207
|
void AudioNode::onInputConnected(AudioNode *node) {
|
|
208
|
+
if (!isInitialized_) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
203
212
|
inputNodes_.push_back(node);
|
|
204
213
|
|
|
205
214
|
if (node->isEnabled()) {
|
|
@@ -208,13 +217,17 @@ void AudioNode::onInputConnected(AudioNode *node) {
|
|
|
208
217
|
}
|
|
209
218
|
|
|
210
219
|
void AudioNode::onInputDisconnected(AudioNode *node) {
|
|
220
|
+
if (!isInitialized_) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
211
224
|
auto position = std::find(inputNodes_.begin(), inputNodes_.end(), node);
|
|
212
225
|
|
|
213
226
|
if (position != inputNodes_.end()) {
|
|
214
227
|
inputNodes_.erase(position);
|
|
215
228
|
}
|
|
216
229
|
|
|
217
|
-
if (inputNodes_.
|
|
230
|
+
if (!inputNodes_.empty()) {
|
|
218
231
|
return;
|
|
219
232
|
}
|
|
220
233
|
|
|
@@ -222,7 +235,7 @@ void AudioNode::onInputDisconnected(AudioNode *node) {
|
|
|
222
235
|
node->onInputDisabled();
|
|
223
236
|
}
|
|
224
237
|
|
|
225
|
-
for (auto outputNode : outputNodes_) {
|
|
238
|
+
for (const auto &outputNode : outputNodes_) {
|
|
226
239
|
disconnectNode(outputNode);
|
|
227
240
|
}
|
|
228
241
|
}
|
|
@@ -23,6 +23,7 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
|
|
|
23
23
|
std::string getChannelCountMode() const;
|
|
24
24
|
std::string getChannelInterpretation() const;
|
|
25
25
|
void connect(const std::shared_ptr<AudioNode> &node);
|
|
26
|
+
void disconnect();
|
|
26
27
|
void disconnect(const std::shared_ptr<AudioNode> &node);
|
|
27
28
|
|
|
28
29
|
bool isEnabled() const;
|
|
@@ -85,8 +85,7 @@ void AudioScheduledSourceNode::updatePlaybackInfo(
|
|
|
85
85
|
startOffset = std::max(startFrame, firstFrame) - firstFrame > 0
|
|
86
86
|
? std::max(startFrame, firstFrame) - firstFrame
|
|
87
87
|
: 0;
|
|
88
|
-
nonSilentFramesToProcess =
|
|
89
|
-
std::min(lastFrame, stopFrame) - startFrame;
|
|
88
|
+
nonSilentFramesToProcess = std::min(lastFrame, stopFrame) - startFrame;
|
|
90
89
|
processingBus->zero(0, startOffset);
|
|
91
90
|
return;
|
|
92
91
|
}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
#ifdef ANDROID
|
|
2
|
-
#include "AudioPlayer.h"
|
|
3
|
-
#else
|
|
4
|
-
#include "IOSAudioPlayer.h"
|
|
5
|
-
#endif
|
|
6
|
-
|
|
7
1
|
#include "BaseAudioContext.h"
|
|
8
2
|
|
|
3
|
+
#include "AnalyserNode.h"
|
|
9
4
|
#include "AudioArray.h"
|
|
10
5
|
#include "AudioBuffer.h"
|
|
11
6
|
#include "AudioBufferSourceNode.h"
|
|
@@ -22,30 +17,10 @@
|
|
|
22
17
|
namespace audioapi {
|
|
23
18
|
|
|
24
19
|
BaseAudioContext::BaseAudioContext() {
|
|
25
|
-
#ifdef ANDROID
|
|
26
|
-
audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio());
|
|
27
|
-
#else
|
|
28
|
-
audioPlayer_ = std::make_shared<IOSAudioPlayer>(this->renderAudio());
|
|
29
|
-
#endif
|
|
30
|
-
|
|
31
|
-
audioDecoder_ = std::make_shared<AudioDecoder>(audioPlayer_->getSampleRate());
|
|
32
|
-
|
|
33
|
-
sampleRate_ = audioPlayer_->getSampleRate();
|
|
34
|
-
bufferSizeInFrames_ = audioPlayer_->getBufferSizeInFrames();
|
|
35
|
-
|
|
36
20
|
nodeManager_ = std::make_shared<AudioNodeManager>();
|
|
37
21
|
destination_ = std::make_shared<AudioDestinationNode>(this);
|
|
38
22
|
}
|
|
39
23
|
|
|
40
|
-
BaseAudioContext::~BaseAudioContext() {
|
|
41
|
-
if (isRunning()) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
state_ = ContextState::CLOSED;
|
|
46
|
-
audioPlayer_->stop();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
24
|
std::string BaseAudioContext::getState() {
|
|
50
25
|
return BaseAudioContext::toString(state_);
|
|
51
26
|
}
|
|
@@ -54,10 +29,6 @@ int BaseAudioContext::getSampleRate() const {
|
|
|
54
29
|
return sampleRate_;
|
|
55
30
|
}
|
|
56
31
|
|
|
57
|
-
int BaseAudioContext::getBufferSizeInFrames() const {
|
|
58
|
-
return bufferSizeInFrames_;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
32
|
std::size_t BaseAudioContext::getCurrentSampleFrame() const {
|
|
62
33
|
return destination_->getCurrentSampleFrame();
|
|
63
34
|
}
|
|
@@ -106,22 +77,16 @@ std::shared_ptr<PeriodicWave> BaseAudioContext::createPeriodicWave(
|
|
|
106
77
|
sampleRate_, real, imag, length, disableNormalization);
|
|
107
78
|
}
|
|
108
79
|
|
|
80
|
+
std::shared_ptr<AnalyserNode> BaseAudioContext::createAnalyser() {
|
|
81
|
+
return std::make_shared<AnalyserNode>(this);
|
|
82
|
+
}
|
|
83
|
+
|
|
109
84
|
std::shared_ptr<AudioBuffer> BaseAudioContext::decodeAudioDataSource(
|
|
110
85
|
const std::string &path) {
|
|
111
86
|
auto audioBus = audioDecoder_->decodeWithFilePath(path);
|
|
112
87
|
return std::make_shared<AudioBuffer>(audioBus);
|
|
113
88
|
}
|
|
114
89
|
|
|
115
|
-
std::function<void(AudioBus *, int)> BaseAudioContext::renderAudio() {
|
|
116
|
-
if (!isRunning()) {
|
|
117
|
-
return [](AudioBus *, int) {};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return [this](AudioBus *data, int frames) {
|
|
121
|
-
destination_->renderAudio(data, frames);
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
90
|
AudioNodeManager *BaseAudioContext::getNodeManager() {
|
|
126
91
|
return nodeManager_.get();
|
|
127
92
|
}
|
|
@@ -22,22 +22,16 @@ class BiquadFilterNode;
|
|
|
22
22
|
class AudioDestinationNode;
|
|
23
23
|
class AudioBufferSourceNode;
|
|
24
24
|
class AudioDecoder;
|
|
25
|
-
|
|
26
|
-
#ifdef ANDROID
|
|
27
|
-
class AudioPlayer;
|
|
28
|
-
#else
|
|
29
|
-
class IOSAudioPlayer;
|
|
30
|
-
#endif
|
|
25
|
+
class AnalyserNode;
|
|
31
26
|
|
|
32
27
|
class BaseAudioContext {
|
|
33
28
|
public:
|
|
34
29
|
BaseAudioContext();
|
|
35
|
-
~BaseAudioContext();
|
|
30
|
+
virtual ~BaseAudioContext() = default;
|
|
36
31
|
|
|
37
32
|
std::string getState();
|
|
38
33
|
[[nodiscard]] int getSampleRate() const;
|
|
39
34
|
[[nodiscard]] double getCurrentTime() const;
|
|
40
|
-
[[nodiscard]] int getBufferSizeInFrames() const;
|
|
41
35
|
[[nodiscard]] std::size_t getCurrentSampleFrame() const;
|
|
42
36
|
std::shared_ptr<AudioDestinationNode> getDestination();
|
|
43
37
|
|
|
@@ -53,10 +47,10 @@ class BaseAudioContext {
|
|
|
53
47
|
float *imag,
|
|
54
48
|
bool disableNormalization,
|
|
55
49
|
int length);
|
|
50
|
+
std::shared_ptr<AnalyserNode> createAnalyser();
|
|
56
51
|
|
|
57
52
|
std::shared_ptr<AudioBuffer> decodeAudioDataSource(const std::string &path);
|
|
58
53
|
std::shared_ptr<PeriodicWave> getBasicWaveForm(OscillatorType type);
|
|
59
|
-
std::function<void(AudioBus *, int)> renderAudio();
|
|
60
54
|
AudioNodeManager *getNodeManager();
|
|
61
55
|
[[nodiscard]] bool isRunning() const;
|
|
62
56
|
[[nodiscard]] bool isClosed() const;
|
|
@@ -64,16 +58,11 @@ class BaseAudioContext {
|
|
|
64
58
|
protected:
|
|
65
59
|
static std::string toString(ContextState state);
|
|
66
60
|
std::shared_ptr<AudioDestinationNode> destination_;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
#ifdef ANDROID
|
|
70
|
-
std::shared_ptr<AudioPlayer> audioPlayer_;
|
|
71
|
-
#else
|
|
72
|
-
std::shared_ptr<IOSAudioPlayer> audioPlayer_;
|
|
73
|
-
#endif
|
|
61
|
+
// init in AudioContext or OfflineContext constructor
|
|
62
|
+
std::shared_ptr<AudioDecoder> audioDecoder_ {};
|
|
74
63
|
|
|
75
|
-
|
|
76
|
-
int
|
|
64
|
+
// init in AudioContext or OfflineContext constructor
|
|
65
|
+
int sampleRate_ {};
|
|
77
66
|
ContextState state_ = ContextState::RUNNING;
|
|
78
67
|
std::shared_ptr<AudioNodeManager> nodeManager_;
|
|
79
68
|
|
|
@@ -6,11 +6,14 @@
|
|
|
6
6
|
// https://webaudio.github.io/web-audio-api/
|
|
7
7
|
|
|
8
8
|
namespace audioapi {
|
|
9
|
-
constexpr int
|
|
9
|
+
constexpr int DEFAULT_SAMPLE_RATE = 48000;
|
|
10
|
+
constexpr int RENDER_QUANTUM_SIZE = 128;
|
|
10
11
|
constexpr int CHANNEL_COUNT = 2;
|
|
12
|
+
|
|
11
13
|
constexpr float MOST_POSITIVE_SINGLE_FLOAT = static_cast<float>(std::numeric_limits<float>::max());
|
|
12
14
|
constexpr float MOST_NEGATIVE_SINGLE_FLOAT = static_cast<float>(std::numeric_limits<float>::lowest());
|
|
13
|
-
|
|
15
|
+
|
|
16
|
+
constexpr float NYQUIST_FREQUENCY = DEFAULT_SAMPLE_RATE / 2.0;
|
|
14
17
|
static float MAX_DETUNE = 1200 * std::log2(MOST_POSITIVE_SINGLE_FLOAT);
|
|
15
18
|
constexpr float MAX_GAIN = MOST_POSITIVE_SINGLE_FLOAT;
|
|
16
19
|
constexpr float MAX_PAN = 1.0;
|
|
@@ -19,4 +22,10 @@ constexpr float MAX_FILTER_FREQUENCY = NYQUIST_FREQUENCY;
|
|
|
19
22
|
constexpr float MIN_FILTER_FREQUENCY = 0.0;
|
|
20
23
|
static float MAX_FILTER_GAIN = 40 * std::log10(MOST_POSITIVE_SINGLE_FLOAT);
|
|
21
24
|
constexpr float MIN_FILTER_GAIN = -MAX_GAIN;
|
|
25
|
+
|
|
26
|
+
constexpr int MAX_FFT_SIZE = 32768;
|
|
27
|
+
constexpr int DEFAULT_FFT_SIZE = 2048;
|
|
28
|
+
constexpr double DEFAULT_MAX_DECIBELS = -30;
|
|
29
|
+
constexpr double DEFAULT_MIN_DECIBELS = -100;
|
|
30
|
+
const double DEFAULT_SMOOTHING_TIME_CONSTANT = 0.8;
|
|
22
31
|
} // namespace audioapi
|
|
@@ -226,7 +226,7 @@ void PeriodicWave::createBandLimitedTables(
|
|
|
226
226
|
0.0f);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
// Zero out the
|
|
229
|
+
// Zero out the DC and nquist components.
|
|
230
230
|
realFFTFrameData[0] = 0.0f;
|
|
231
231
|
imaginaryFFTFrameData[0] = 0.0f;
|
|
232
232
|
|
|
@@ -234,7 +234,7 @@ void PeriodicWave::createBandLimitedTables(
|
|
|
234
234
|
|
|
235
235
|
// Perform the inverse FFT to get the time domain representation of the
|
|
236
236
|
// band-limited waveform.
|
|
237
|
-
fftFrame.
|
|
237
|
+
fftFrame.doInverseFFT(bandLimitedTables_[rangeIndex]);
|
|
238
238
|
|
|
239
239
|
if (!disableNormalization_ && rangeIndex == 0) {
|
|
240
240
|
float maxValue =
|
|
@@ -23,4 +23,11 @@ float linearInterpolate(
|
|
|
23
23
|
factor * (source[secondIndex] - source[firstIndex]);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
float linearToDecibels(float value) {
|
|
27
|
+
return 20 * log10f(value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
float decibelsToLinear(float value) {
|
|
31
|
+
return powf(10, value / 20);
|
|
32
|
+
}
|
|
26
33
|
} // namespace audioapi::AudioUtils
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <cstddef>
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
#include <cmath>
|
|
4
6
|
|
|
5
7
|
namespace audioapi::AudioUtils {
|
|
6
|
-
size_t timeToSampleFrame(double time, int sampleRate);
|
|
7
8
|
|
|
9
|
+
size_t timeToSampleFrame(double time, int sampleRate);
|
|
8
10
|
double sampleFrameToTime(int sampleFrame, int sampleRate);
|
|
9
11
|
|
|
10
12
|
float linearInterpolate(const float *source, size_t firstIndex, size_t secondIndex, float factor);
|
|
11
13
|
|
|
14
|
+
float linearToDecibels(float value);
|
|
15
|
+
float decibelsToLinear(float value);
|
|
12
16
|
} // namespace audioapi::AudioUtils
|
|
@@ -10,46 +10,92 @@
|
|
|
10
10
|
|
|
11
11
|
namespace audioapi {
|
|
12
12
|
#if defined(HAVE_ACCELERATE)
|
|
13
|
+
static std::unordered_map<size_t, FFTSetup> fftSetups_;
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
FFTFrame::FFTFrame(int size)
|
|
16
|
+
: size_(size),
|
|
17
|
+
log2Size_(static_cast<int>(log2(size))),
|
|
18
|
+
realData_(new float[size]),
|
|
19
|
+
imaginaryData_(new float[size]) {
|
|
20
|
+
fftSetup_ = getFFTSetupForSize(log2Size_);
|
|
21
|
+
frame_.realp = realData_;
|
|
22
|
+
frame_.imagp = imaginaryData_;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
FFTFrame::~FFTFrame() {
|
|
26
|
+
delete[] realData_;
|
|
27
|
+
delete[] imaginaryData_;
|
|
28
|
+
}
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
FFTSetup FFTFrame::getFFTSetupForSize(size_t log2FFTSize) {
|
|
31
|
+
if (!fftSetups_.contains(log2FFTSize)) {
|
|
32
|
+
fftSetups_.emplace(
|
|
33
|
+
log2FFTSize, vDSP_create_fftsetup(log2FFTSize, FFT_RADIX2));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return fftSetups_.at(log2FFTSize);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void FFTFrame::doFFT(float *data) {
|
|
40
|
+
vDSP_ctoz(reinterpret_cast<DSPComplex *>(data), 2, &frame_, 1, size_ / 2);
|
|
41
|
+
vDSP_fft_zrip(fftSetup_, &frame_, 1, log2Size_, FFT_FORWARD);
|
|
42
|
+
|
|
43
|
+
VectorMath::multiplyByScalar(realData_, 0.5f, realData_, size_ / 2);
|
|
44
|
+
VectorMath::multiplyByScalar(imaginaryData_, 0.5f, imaginaryData_, size_ / 2);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
void FFTFrame::doInverseFFT(float *data) {
|
|
48
|
+
vDSP_fft_zrip(fftSetup_, &frame_, 1, log2Size_, FFT_INVERSE);
|
|
49
|
+
vDSP_ztoc(&frame_, 1, reinterpret_cast<DSPComplex *>(data), 2, size_ / 2);
|
|
27
50
|
|
|
28
51
|
// Scale the FFT data, beacuse of
|
|
29
52
|
// https://developer.apple.com/library/archive/documentation/Performance/Conceptual/vDSP_Programming_Guide/UsingFourierTransforms/UsingFourierTransforms.html#//apple_ref/doc/uid/TP40005147-CH3-15892
|
|
30
53
|
VectorMath::multiplyByScalar(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
vDSP_destroy_fftsetup(fftSetup_);
|
|
54
|
+
data, 1.0f / static_cast<float>(size_), data, size_);
|
|
34
55
|
}
|
|
35
56
|
|
|
36
57
|
#elif defined(ANDROID)
|
|
37
58
|
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
FFTFrame::FFTFrame(int size)
|
|
60
|
+
: size_(size),
|
|
61
|
+
log2Size_(static_cast<int>(log2(size))),
|
|
62
|
+
realData_(new float[size]),
|
|
63
|
+
imaginaryData_(new float[size]) {
|
|
64
|
+
frame_ = fftwf_alloc_complex(size / 2);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
FFTFrame::~FFTFrame() {
|
|
68
|
+
delete[] realData_;
|
|
69
|
+
delete[] imaginaryData_;
|
|
70
|
+
fftwf_free(frame_);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void FFTFrame::doFFT(float *data) {
|
|
74
|
+
auto plan = fftwf_plan_dft_r2c_1d(size_, data, frame_, FFTW_ESTIMATE);
|
|
75
|
+
fftwf_execute(plan);
|
|
76
|
+
fftwf_destroy_plan(plan);
|
|
77
|
+
|
|
78
|
+
for (int i = 0; i < size_ / 2; ++i) {
|
|
79
|
+
realData_[i] = frame_[i][0];
|
|
80
|
+
imaginaryData_[i] = frame_[i][1];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
VectorMath::multiplyByScalar(realData_, 0.5f, realData_, size_ / 2);
|
|
84
|
+
VectorMath::multiplyByScalar(imaginaryData_, 0.5f, imaginaryData_, size_ / 2);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
void FFTFrame::doInverseFFT(float *data) {
|
|
40
88
|
for (int i = 0; i < size_ / 2; i++) {
|
|
41
|
-
|
|
42
|
-
|
|
89
|
+
frame_[i][0] = realData_[i];
|
|
90
|
+
frame_[i][1] = imaginaryData_[i];
|
|
43
91
|
}
|
|
44
92
|
|
|
45
|
-
auto plan = fftwf_plan_dft_c2r_1d(
|
|
46
|
-
size_, freqDomainData, timeDomainData, FFTW_ESTIMATE);
|
|
93
|
+
auto plan = fftwf_plan_dft_c2r_1d(size_, frame_, data, FFTW_ESTIMATE);
|
|
47
94
|
fftwf_execute(plan);
|
|
48
95
|
fftwf_destroy_plan(plan);
|
|
49
|
-
fftwf_free(freqDomainData);
|
|
50
96
|
|
|
51
97
|
VectorMath::multiplyByScalar(
|
|
52
|
-
|
|
98
|
+
data, 1.0f / static_cast<float>(size_), data, size_);
|
|
53
99
|
}
|
|
54
100
|
|
|
55
101
|
#endif
|
|
@@ -31,22 +31,24 @@
|
|
|
31
31
|
#include <algorithm>
|
|
32
32
|
#include <cmath>
|
|
33
33
|
#include <utility>
|
|
34
|
+
#include <unordered_map>
|
|
34
35
|
|
|
35
36
|
#include "VectorMath.h"
|
|
36
37
|
|
|
38
|
+
#if defined(HAVE_ACCELERATE)
|
|
39
|
+
#include <Accelerate/Accelerate.h>
|
|
40
|
+
#endif
|
|
41
|
+
|
|
42
|
+
#if defined(ANDROID)
|
|
43
|
+
#include <fftw3.h>
|
|
44
|
+
#endif
|
|
45
|
+
|
|
37
46
|
namespace audioapi {
|
|
38
47
|
|
|
39
48
|
class FFTFrame {
|
|
40
49
|
public:
|
|
41
|
-
explicit FFTFrame(int size)
|
|
42
|
-
|
|
43
|
-
log2Size_(static_cast<int>(log2(size))),
|
|
44
|
-
realData_(new float[size]),
|
|
45
|
-
imaginaryData_(new float[size]) {}
|
|
46
|
-
~FFTFrame() {
|
|
47
|
-
delete[] realData_;
|
|
48
|
-
delete[] imaginaryData_;
|
|
49
|
-
}
|
|
50
|
+
explicit FFTFrame(int size);
|
|
51
|
+
~FFTFrame();
|
|
50
52
|
|
|
51
53
|
[[nodiscard]] float *getRealData() const {
|
|
52
54
|
return realData_;
|
|
@@ -55,13 +57,26 @@ class FFTFrame {
|
|
|
55
57
|
return imaginaryData_;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
void
|
|
60
|
+
void doFFT(float *data);
|
|
61
|
+
|
|
62
|
+
void doInverseFFT(float *data);
|
|
59
63
|
|
|
60
64
|
private:
|
|
61
65
|
int size_;
|
|
62
66
|
int log2Size_;
|
|
63
67
|
float *realData_;
|
|
64
68
|
float *imaginaryData_;
|
|
69
|
+
|
|
70
|
+
#if defined(HAVE_ACCELERATE)
|
|
71
|
+
FFTSetup fftSetup_;
|
|
72
|
+
DSPSplitComplex frame_;
|
|
73
|
+
|
|
74
|
+
static FFTSetup getFFTSetupForSize(size_t log2FFTSize);
|
|
75
|
+
#endif
|
|
76
|
+
|
|
77
|
+
#if defined(ANDROID)
|
|
78
|
+
fftwf_complex *frame_;
|
|
79
|
+
#endif
|
|
65
80
|
};
|
|
66
81
|
|
|
67
82
|
} // namespace audioapi
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
#include "VectorMath.h"
|
|
27
|
+
#include "AudioUtils.h"
|
|
27
28
|
|
|
28
29
|
#if defined(HAVE_ACCELERATE)
|
|
29
30
|
#include <Accelerate/Accelerate.h>
|
|
@@ -691,4 +692,13 @@ void multiplyByScalarThenAddToOutput(
|
|
|
691
692
|
}
|
|
692
693
|
|
|
693
694
|
#endif
|
|
695
|
+
|
|
696
|
+
void linearToDecibels(
|
|
697
|
+
const float *inputVector,
|
|
698
|
+
float *outputVector,
|
|
699
|
+
size_t numberOfElementsToProcess) {
|
|
700
|
+
for (int i = 0; i < numberOfElementsToProcess; i++) {
|
|
701
|
+
outputVector[i] = AudioUtils::linearToDecibels(inputVector[i]);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
694
704
|
} // namespace audioapi::VectorMath
|
|
@@ -42,4 +42,6 @@ void multiply(const float *inputVector1, const float *inputVector2, float *outpu
|
|
|
42
42
|
|
|
43
43
|
// Finds the maximum magnitude of a float vector.
|
|
44
44
|
float maximumMagnitude(const float *inputVector, size_t numberOfElementsToProcess);
|
|
45
|
+
|
|
46
|
+
void linearToDecibels(const float *inputVector, float *outputVector, size_t numberOfElementsToProcess);
|
|
45
47
|
} // namespace audioapi::VectorMath
|
package/ios/core/AudioPlayer.h
CHANGED
|
@@ -12,12 +12,13 @@ typedef void (^RenderAudioBlock)(AudioBufferList *outputBuffer, int numFrames);
|
|
|
12
12
|
@property (nonatomic, strong) AVAudioFormat *format;
|
|
13
13
|
@property (nonatomic, strong) AVAudioSourceNode *sourceNode;
|
|
14
14
|
@property (nonatomic, copy) RenderAudioBlock renderAudio;
|
|
15
|
+
@property (nonatomic, assign) int sampleRate;
|
|
15
16
|
|
|
16
17
|
- (instancetype)initWithRenderAudioBlock:(RenderAudioBlock)renderAudio;
|
|
17
18
|
|
|
18
|
-
- (int)
|
|
19
|
+
- (instancetype)initWithRenderAudioBlock:(RenderAudioBlock)renderAudio sampleRate:(int)sampleRate;
|
|
19
20
|
|
|
20
|
-
- (int)
|
|
21
|
+
- (int)getSampleRate;
|
|
21
22
|
|
|
22
23
|
- (void)start;
|
|
23
24
|
|