react-native-audio-api 0.4.0 → 0.4.2
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/src/main/cpp/core/AudioPlayer.cpp +38 -11
- package/android/src/main/cpp/core/AudioPlayer.h +6 -2
- package/common/cpp/HostObjects/AnalyserNodeHostObject.h +19 -6
- package/common/cpp/HostObjects/AudioAPIInstallerHostObject.h +11 -3
- package/common/cpp/HostObjects/AudioBufferHostObject.h +6 -5
- package/common/cpp/HostObjects/AudioNodeHostObject.h +5 -0
- package/common/cpp/HostObjects/AudioParamHostObject.h +2 -1
- package/common/cpp/HostObjects/BaseAudioContextHostObject.h +4 -3
- package/common/cpp/core/AnalyserNode.cpp +56 -34
- package/common/cpp/core/AnalyserNode.h +51 -19
- package/common/cpp/core/AudioArray.cpp +14 -14
- package/common/cpp/core/AudioArray.h +16 -15
- package/common/cpp/core/AudioBuffer.cpp +12 -9
- package/common/cpp/core/AudioBuffer.h +9 -8
- package/common/cpp/core/AudioBufferSourceNode.cpp +25 -20
- package/common/cpp/core/AudioBufferSourceNode.h +1 -0
- package/common/cpp/core/AudioBus.cpp +22 -26
- package/common/cpp/core/AudioBus.h +24 -24
- package/common/cpp/core/AudioContext.cpp +41 -1
- package/common/cpp/core/AudioContext.h +17 -0
- package/common/cpp/core/AudioDecoder.h +2 -2
- package/common/cpp/core/AudioDestinationNode.cpp +1 -1
- package/common/cpp/core/AudioDestinationNode.h +2 -1
- package/common/cpp/core/AudioNode.cpp +22 -8
- package/common/cpp/core/AudioNode.h +10 -10
- package/common/cpp/core/AudioNodeManager.cpp +1 -3
- package/common/cpp/core/AudioNodeManager.h +1 -1
- package/common/cpp/core/AudioParam.cpp +6 -3
- package/common/cpp/core/AudioParam.h +2 -1
- package/common/cpp/core/AudioScheduledSourceNode.cpp +1 -1
- package/common/cpp/core/AudioScheduledSourceNode.h +1 -0
- package/common/cpp/core/BaseAudioContext.cpp +7 -43
- package/common/cpp/core/BaseAudioContext.h +10 -21
- package/common/cpp/core/BiquadFilterNode.cpp +13 -14
- package/common/cpp/core/Constants.h +26 -12
- package/common/cpp/core/GainNode.cpp +1 -1
- package/common/cpp/core/OscillatorNode.cpp +4 -3
- package/common/cpp/core/PeriodicWave.cpp +7 -6
- package/common/cpp/core/PeriodicWave.h +4 -4
- package/common/cpp/core/StereoPannerNode.cpp +4 -4
- package/common/cpp/jsi/JsiHostObject.h +1 -1
- package/common/cpp/jsi/JsiPromise.h +1 -0
- package/common/cpp/utils/AudioUtils.cpp +2 -2
- package/common/cpp/utils/AudioUtils.h +2 -2
- package/common/cpp/utils/Locker.h +2 -2
- package/common/cpp/utils/VectorMath.cpp +1 -1
- package/ios/core/AudioPlayer.h +3 -2
- package/ios/core/AudioPlayer.m +49 -15
- package/ios/core/IOSAudioPlayer.h +4 -2
- package/ios/core/IOSAudioPlayer.mm +47 -11
- package/lib/module/core/AnalyserNode.js +6 -0
- package/lib/module/core/AnalyserNode.js.map +1 -1
- 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/index.js +16 -6
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.native.js +1 -1
- package/lib/module/index.native.js.map +1 -1
- package/lib/typescript/core/AnalyserNode.d.ts +3 -0
- package/lib/typescript/core/AnalyserNode.d.ts.map +1 -1
- 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/types.d.ts +1 -0
- package/lib/typescript/core/types.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +6 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.native.d.ts +1 -1
- package/lib/typescript/index.native.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +3 -2
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/AnalyserNode.ts +9 -0
- package/src/core/AudioContext.ts +2 -2
- package/src/core/AudioNode.ts +5 -5
- package/src/core/types.ts +2 -0
- package/src/index.native.ts +1 -0
- package/src/index.ts +26 -7
- package/src/interfaces.ts +3 -1
- package/src/specs/global.d.ts +1 -1
|
@@ -21,17 +21,36 @@ AudioPlayer::AudioPlayer(
|
|
|
21
21
|
->setDataCallback(this)
|
|
22
22
|
->openStream(mStream_);
|
|
23
23
|
|
|
24
|
+
sampleRate_ = static_cast<float>(mStream_->getSampleRate());
|
|
24
25
|
mBus_ = std::make_shared<AudioBus>(
|
|
25
|
-
|
|
26
|
+
sampleRate_, RENDER_QUANTUM_SIZE, CHANNEL_COUNT);
|
|
26
27
|
isInitialized_ = true;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
AudioPlayer::AudioPlayer(
|
|
31
|
+
const std::function<void(AudioBus *, int)> &renderAudio,
|
|
32
|
+
float sampleRate)
|
|
33
|
+
: renderAudio_(renderAudio) {
|
|
34
|
+
AudioStreamBuilder builder;
|
|
35
|
+
|
|
36
|
+
builder.setSharingMode(SharingMode::Exclusive)
|
|
37
|
+
->setFormat(AudioFormat::Float)
|
|
38
|
+
->setFormatConversionAllowed(true)
|
|
39
|
+
->setPerformanceMode(PerformanceMode::LowLatency)
|
|
40
|
+
->setChannelCount(CHANNEL_COUNT)
|
|
41
|
+
->setSampleRateConversionQuality(SampleRateConversionQuality::Medium)
|
|
42
|
+
->setDataCallback(this)
|
|
43
|
+
->setSampleRate(static_cast<int>(sampleRate))
|
|
44
|
+
->openStream(mStream_);
|
|
45
|
+
|
|
46
|
+
sampleRate_ = sampleRate;
|
|
47
|
+
mBus_ = std::make_shared<AudioBus>(
|
|
48
|
+
sampleRate_, RENDER_QUANTUM_SIZE, CHANNEL_COUNT);
|
|
49
|
+
isInitialized_ = true;
|
|
31
50
|
}
|
|
32
51
|
|
|
33
|
-
|
|
34
|
-
return
|
|
52
|
+
float AudioPlayer::getSampleRate() const {
|
|
53
|
+
return sampleRate_;
|
|
35
54
|
}
|
|
36
55
|
|
|
37
56
|
void AudioPlayer::start() {
|
|
@@ -59,14 +78,22 @@ DataCallbackResult AudioPlayer::onAudioReady(
|
|
|
59
78
|
}
|
|
60
79
|
|
|
61
80
|
auto buffer = static_cast<float *>(audioData);
|
|
62
|
-
|
|
81
|
+
int processedFrames = 0;
|
|
63
82
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
while (processedFrames < numFrames) {
|
|
84
|
+
int framesToProcess =
|
|
85
|
+
std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE);
|
|
86
|
+
renderAudio_(mBus_.get(), framesToProcess);
|
|
87
|
+
|
|
88
|
+
// TODO: optimize this with SIMD?
|
|
89
|
+
for (int i = 0; i < framesToProcess; i++) {
|
|
90
|
+
for (int channel = 0; channel < CHANNEL_COUNT; channel += 1) {
|
|
91
|
+
buffer[(processedFrames + i) * CHANNEL_COUNT + channel] =
|
|
92
|
+
mBus_->getChannel(channel)->getData()[i];
|
|
93
|
+
}
|
|
69
94
|
}
|
|
95
|
+
|
|
96
|
+
processedFrames += framesToProcess;
|
|
70
97
|
}
|
|
71
98
|
|
|
72
99
|
return DataCallbackResult::Continue;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <oboe/Oboe.h>
|
|
4
|
+
#include <functional>
|
|
4
5
|
#include <memory>
|
|
5
6
|
|
|
6
7
|
namespace audioapi {
|
|
@@ -13,9 +14,11 @@ class AudioBus;
|
|
|
13
14
|
class AudioPlayer : public AudioStreamDataCallback {
|
|
14
15
|
public:
|
|
15
16
|
explicit AudioPlayer(const std::function<void(AudioBus *, int)> &renderAudio);
|
|
17
|
+
AudioPlayer(
|
|
18
|
+
const std::function<void(AudioBus *, int)> &renderAudio,
|
|
19
|
+
float sampleRate);
|
|
16
20
|
|
|
17
|
-
[[nodiscard]]
|
|
18
|
-
[[nodiscard]] int getBufferSizeInFrames() const;
|
|
21
|
+
[[nodiscard]] float getSampleRate() const;
|
|
19
22
|
void start();
|
|
20
23
|
void stop();
|
|
21
24
|
|
|
@@ -29,6 +32,7 @@ class AudioPlayer : public AudioStreamDataCallback {
|
|
|
29
32
|
std::shared_ptr<AudioStream> mStream_;
|
|
30
33
|
std::shared_ptr<AudioBus> mBus_;
|
|
31
34
|
bool isInitialized_ = false;
|
|
35
|
+
float sampleRate_;
|
|
32
36
|
};
|
|
33
37
|
|
|
34
38
|
} // namespace audioapi
|
|
@@ -19,7 +19,8 @@ class AnalyserNodeHostObject : public AudioNodeHostObject {
|
|
|
19
19
|
JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, frequencyBinCount),
|
|
20
20
|
JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, minDecibels),
|
|
21
21
|
JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, maxDecibels),
|
|
22
|
-
JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, smoothingTimeConstant)
|
|
22
|
+
JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, smoothingTimeConstant),
|
|
23
|
+
JSI_EXPORT_PROPERTY_GETTER(AnalyserNodeHostObject, window));
|
|
23
24
|
|
|
24
25
|
addFunctions(
|
|
25
26
|
JSI_EXPORT_FUNCTION(
|
|
@@ -36,7 +37,8 @@ class AnalyserNodeHostObject : public AudioNodeHostObject {
|
|
|
36
37
|
JSI_EXPORT_PROPERTY_SETTER(AnalyserNodeHostObject, minDecibels),
|
|
37
38
|
JSI_EXPORT_PROPERTY_SETTER(AnalyserNodeHostObject, maxDecibels),
|
|
38
39
|
JSI_EXPORT_PROPERTY_SETTER(
|
|
39
|
-
AnalyserNodeHostObject, smoothingTimeConstant)
|
|
40
|
+
AnalyserNodeHostObject, smoothingTimeConstant),
|
|
41
|
+
JSI_EXPORT_PROPERTY_SETTER(AnalyserNodeHostObject, window));
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
JSI_PROPERTY_GETTER(fftSize) {
|
|
@@ -64,6 +66,12 @@ class AnalyserNodeHostObject : public AudioNodeHostObject {
|
|
|
64
66
|
return {analyserNode->getSmoothingTimeConstant()};
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
JSI_PROPERTY_GETTER(window) {
|
|
70
|
+
auto analyserNode = std::static_pointer_cast<AnalyserNode>(node_);
|
|
71
|
+
auto windowType = analyserNode->getWindowType();
|
|
72
|
+
return jsi::String::createFromUtf8(runtime, windowType);
|
|
73
|
+
}
|
|
74
|
+
|
|
67
75
|
JSI_HOST_FUNCTION(getFloatFrequencyData) {
|
|
68
76
|
auto destination = args[0].getObject(runtime).asArray(runtime);
|
|
69
77
|
auto length = static_cast<int>(destination.getProperty(runtime, "length").asNumber());
|
|
@@ -126,26 +134,31 @@ class AnalyserNodeHostObject : public AudioNodeHostObject {
|
|
|
126
134
|
|
|
127
135
|
JSI_PROPERTY_SETTER(fftSize) {
|
|
128
136
|
auto analyserNode = std::static_pointer_cast<AnalyserNode>(node_);
|
|
129
|
-
auto fftSize = static_cast<
|
|
137
|
+
auto fftSize = static_cast<int>(value.getNumber());
|
|
130
138
|
analyserNode->setFftSize(fftSize);
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
JSI_PROPERTY_SETTER(minDecibels) {
|
|
134
142
|
auto analyserNode = std::static_pointer_cast<AnalyserNode>(node_);
|
|
135
|
-
auto minDecibels = static_cast<
|
|
143
|
+
auto minDecibels = static_cast<float>(value.getNumber());
|
|
136
144
|
analyserNode->setMinDecibels(minDecibels);
|
|
137
145
|
}
|
|
138
146
|
|
|
139
147
|
JSI_PROPERTY_SETTER(maxDecibels) {
|
|
140
148
|
auto analyserNode = std::static_pointer_cast<AnalyserNode>(node_);
|
|
141
|
-
auto maxDecibels = static_cast<
|
|
149
|
+
auto maxDecibels = static_cast<float>(value.getNumber());
|
|
142
150
|
analyserNode->setMaxDecibels(maxDecibels);
|
|
143
151
|
}
|
|
144
152
|
|
|
145
153
|
JSI_PROPERTY_SETTER(smoothingTimeConstant) {
|
|
146
154
|
auto analyserNode = std::static_pointer_cast<AnalyserNode>(node_);
|
|
147
|
-
auto smoothingTimeConstant = static_cast<
|
|
155
|
+
auto smoothingTimeConstant = static_cast<float>(value.getNumber());
|
|
148
156
|
analyserNode->setSmoothingTimeConstant(smoothingTimeConstant);
|
|
149
157
|
}
|
|
158
|
+
|
|
159
|
+
JSI_PROPERTY_SETTER(window) {
|
|
160
|
+
auto analyserNode = std::static_pointer_cast<AnalyserNode>(node_);
|
|
161
|
+
analyserNode->setWindowType(value.getString(runtime).utf8(runtime));
|
|
162
|
+
}
|
|
150
163
|
};
|
|
151
164
|
} // namespace audioapi
|
|
@@ -35,10 +35,18 @@ class AudioAPIInstallerHostObject
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
JSI_HOST_FUNCTION(createAudioContext) {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
std::shared_ptr<AudioContext> audioContext;
|
|
39
|
+
if (args[0].isUndefined()) {
|
|
40
|
+
audioContext = std::make_shared<AudioContext>();
|
|
41
|
+
} else {
|
|
42
|
+
auto sampleRate = static_cast<float>(args[0].getNumber());
|
|
43
|
+
audioContext = std::make_shared<AudioContext>(sampleRate);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
auto audioContextHostObject =
|
|
40
47
|
std::make_shared<AudioContextHostObject>(audioContext, promiseVendor_);
|
|
41
|
-
|
|
48
|
+
|
|
49
|
+
return jsi::Object::createFromHostObject(runtime, audioContextHostObject);
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
private:
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <jsi/jsi.h>
|
|
4
4
|
#include <memory>
|
|
5
5
|
#include <vector>
|
|
6
|
+
#include <cstddef>
|
|
6
7
|
|
|
7
8
|
#include <JsiHostObject.h>
|
|
8
9
|
#include "AudioBuffer.h"
|
|
@@ -34,7 +35,7 @@ class AudioBufferHostObject : public JsiHostObject {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
JSI_PROPERTY_GETTER(length) {
|
|
37
|
-
return {audioBuffer_->getLength()};
|
|
38
|
+
return {static_cast<double>(audioBuffer_->getLength())};
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
JSI_PROPERTY_GETTER(duration) {
|
|
@@ -60,9 +61,9 @@ class AudioBufferHostObject : public JsiHostObject {
|
|
|
60
61
|
JSI_HOST_FUNCTION(copyFromChannel) {
|
|
61
62
|
auto destination = args[0].getObject(runtime).asArray(runtime);
|
|
62
63
|
auto destinationLength =
|
|
63
|
-
static_cast<
|
|
64
|
+
static_cast<size_t>(destination.getProperty(runtime, "length").asNumber());
|
|
64
65
|
auto channelNumber = static_cast<int>(args[1].getNumber());
|
|
65
|
-
auto startInChannel = static_cast<
|
|
66
|
+
auto startInChannel = static_cast<size_t>(args[2].getNumber());
|
|
66
67
|
|
|
67
68
|
auto *destinationData = new float[destinationLength];
|
|
68
69
|
|
|
@@ -79,9 +80,9 @@ class AudioBufferHostObject : public JsiHostObject {
|
|
|
79
80
|
JSI_HOST_FUNCTION(copyToChannel) {
|
|
80
81
|
auto source = args[0].getObject(runtime).asArray(runtime);
|
|
81
82
|
auto sourceLength =
|
|
82
|
-
static_cast<
|
|
83
|
+
static_cast<size_t>(source.getProperty(runtime, "length").asNumber());
|
|
83
84
|
auto channelNumber = static_cast<int>(args[1].getNumber());
|
|
84
|
-
auto startInChannel = static_cast<
|
|
85
|
+
auto startInChannel = static_cast<size_t>(args[2].getNumber());
|
|
85
86
|
|
|
86
87
|
auto *sourceData = new float[sourceLength];
|
|
87
88
|
|
|
@@ -54,6 +54,11 @@ class AudioNodeHostObject : public JsiHostObject {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
JSI_HOST_FUNCTION(disconnect) {
|
|
57
|
+
if(args[0].isUndefined()) {
|
|
58
|
+
node_->disconnect();
|
|
59
|
+
return jsi::Value::undefined();
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
auto node =
|
|
58
63
|
args[0].getObject(runtime).getHostObject<AudioNodeHostObject>(runtime);
|
|
59
64
|
node_->disconnect(std::shared_ptr<AudioNodeHostObject>(node)->node_);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <jsi/jsi.h>
|
|
4
4
|
#include <memory>
|
|
5
5
|
#include <vector>
|
|
6
|
+
#include <cstddef>
|
|
6
7
|
|
|
7
8
|
#include <JsiHostObject.h>
|
|
8
9
|
#include "AudioParam.h"
|
|
@@ -79,7 +80,7 @@ class AudioParamHostObject : public JsiHostObject {
|
|
|
79
80
|
|
|
80
81
|
JSI_HOST_FUNCTION(setValueCurveAtTime) {
|
|
81
82
|
auto values = args[0].getObject(runtime).asArray(runtime);
|
|
82
|
-
auto length = static_cast<
|
|
83
|
+
auto length = static_cast<size_t>(values.length(runtime));
|
|
83
84
|
auto valuesData = new float[length];
|
|
84
85
|
for (size_t i = 0; i < values.length(runtime); i++) {
|
|
85
86
|
valuesData[i] =
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include <memory>
|
|
5
5
|
#include <utility>
|
|
6
6
|
#include <vector>
|
|
7
|
+
#include <cstddef>
|
|
7
8
|
|
|
8
9
|
#include <JsiHostObject.h>
|
|
9
10
|
#include <JsiPromise.h>
|
|
@@ -100,9 +101,9 @@ class BaseAudioContextHostObject : public JsiHostObject {
|
|
|
100
101
|
|
|
101
102
|
JSI_HOST_FUNCTION(createBuffer) {
|
|
102
103
|
auto numberOfChannels = static_cast<int>(args[0].getNumber());
|
|
103
|
-
auto length = static_cast<
|
|
104
|
-
auto sampleRate = static_cast<
|
|
105
|
-
auto buffer =
|
|
104
|
+
auto length = static_cast<size_t>(args[1].getNumber());
|
|
105
|
+
auto sampleRate = static_cast<float>(args[2].getNumber());
|
|
106
|
+
auto buffer = BaseAudioContext::createBuffer(numberOfChannels, length, sampleRate);
|
|
106
107
|
auto bufferHostObject = std::make_shared<AudioBufferHostObject>(buffer);
|
|
107
108
|
return jsi::Object::createFromHostObject(runtime, bufferHostObject);
|
|
108
109
|
}
|
|
@@ -16,34 +16,43 @@ AnalyserNode::AnalyserNode(audioapi::BaseAudioContext *context)
|
|
|
16
16
|
minDecibels_(DEFAULT_MIN_DECIBELS),
|
|
17
17
|
maxDecibels_(DEFAULT_MAX_DECIBELS),
|
|
18
18
|
smoothingTimeConstant_(DEFAULT_SMOOTHING_TIME_CONSTANT),
|
|
19
|
+
windowType_(WindowType::BLACKMAN),
|
|
19
20
|
vWriteIndex_(0) {
|
|
20
21
|
inputBuffer_ = std::make_unique<AudioArray>(MAX_FFT_SIZE * 2);
|
|
21
|
-
fftFrame_ = std::make_unique<FFTFrame>(fftSize_);
|
|
22
22
|
magnitudeBuffer_ = std::make_unique<AudioArray>(fftSize_ / 2);
|
|
23
|
+
downMixBus_ = std::make_unique<AudioBus>(
|
|
24
|
+
context_->getSampleRate(), RENDER_QUANTUM_SIZE, 1);
|
|
25
|
+
|
|
26
|
+
fftFrame_ = std::make_unique<FFTFrame>(fftSize_);
|
|
27
|
+
|
|
23
28
|
isInitialized_ = true;
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
int AnalyserNode::getFftSize() const {
|
|
27
32
|
return fftSize_;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
int AnalyserNode::getFrequencyBinCount() const {
|
|
31
36
|
return fftSize_ / 2;
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
float AnalyserNode::getMinDecibels() const {
|
|
35
40
|
return minDecibels_;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
float AnalyserNode::getMaxDecibels() const {
|
|
39
44
|
return maxDecibels_;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
float AnalyserNode::getSmoothingTimeConstant() const {
|
|
43
48
|
return smoothingTimeConstant_;
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
std::string AnalyserNode::getWindowType() const {
|
|
52
|
+
return AnalyserNode::toString(windowType_);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void AnalyserNode::setFftSize(int fftSize) {
|
|
47
56
|
if (fftSize_ == fftSize) {
|
|
48
57
|
return;
|
|
49
58
|
}
|
|
@@ -53,35 +62,39 @@ void AnalyserNode::setFftSize(size_t fftSize) {
|
|
|
53
62
|
magnitudeBuffer_ = std::make_unique<AudioArray>(fftSize_ / 2);
|
|
54
63
|
}
|
|
55
64
|
|
|
56
|
-
void AnalyserNode::setMinDecibels(
|
|
65
|
+
void AnalyserNode::setMinDecibels(float minDecibels) {
|
|
57
66
|
minDecibels_ = minDecibels;
|
|
58
67
|
}
|
|
59
68
|
|
|
60
|
-
void AnalyserNode::setMaxDecibels(
|
|
69
|
+
void AnalyserNode::setMaxDecibels(float maxDecibels) {
|
|
61
70
|
maxDecibels_ = maxDecibels;
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
void AnalyserNode::setSmoothingTimeConstant(
|
|
73
|
+
void AnalyserNode::setSmoothingTimeConstant(float smoothingTimeConstant) {
|
|
65
74
|
smoothingTimeConstant_ = smoothingTimeConstant;
|
|
66
75
|
}
|
|
67
76
|
|
|
68
|
-
void AnalyserNode::
|
|
77
|
+
void AnalyserNode::setWindowType(const std::string &type) {
|
|
78
|
+
windowType_ = AnalyserNode::fromString(type);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
void AnalyserNode::getFloatFrequencyData(float *data, int length) {
|
|
69
82
|
doFFTAnalysis();
|
|
70
83
|
|
|
71
|
-
length = std::min<
|
|
84
|
+
length = std::min(static_cast<int>(magnitudeBuffer_->getSize()), length);
|
|
72
85
|
VectorMath::linearToDecibels(magnitudeBuffer_->getData(), data, length);
|
|
73
86
|
}
|
|
74
87
|
|
|
75
|
-
void AnalyserNode::getByteFrequencyData(uint8_t *data,
|
|
88
|
+
void AnalyserNode::getByteFrequencyData(uint8_t *data, int length) {
|
|
76
89
|
doFFTAnalysis();
|
|
77
90
|
|
|
78
91
|
auto magnitudeBufferData = magnitudeBuffer_->getData();
|
|
79
|
-
length = std::min<
|
|
92
|
+
length = std::min(static_cast<int>(magnitudeBuffer_->getSize()), length);
|
|
80
93
|
|
|
81
94
|
const auto rangeScaleFactor =
|
|
82
95
|
maxDecibels_ == minDecibels_ ? 1 : 1 / (maxDecibels_ - minDecibels_);
|
|
83
96
|
|
|
84
|
-
for (
|
|
97
|
+
for (int i = 0; i < length; i++) {
|
|
85
98
|
auto dbMag = magnitudeBufferData[i] == 0
|
|
86
99
|
? minDecibels_
|
|
87
100
|
: AudioUtils::linearToDecibels(magnitudeBufferData[i]);
|
|
@@ -98,20 +111,20 @@ void AnalyserNode::getByteFrequencyData(uint8_t *data, size_t length) {
|
|
|
98
111
|
}
|
|
99
112
|
}
|
|
100
113
|
|
|
101
|
-
void AnalyserNode::getFloatTimeDomainData(float *data,
|
|
114
|
+
void AnalyserNode::getFloatTimeDomainData(float *data, int length) {
|
|
102
115
|
auto size = std::min(fftSize_, length);
|
|
103
116
|
|
|
104
|
-
for (
|
|
117
|
+
for (int i = 0; i < size; i++) {
|
|
105
118
|
data[i] = inputBuffer_->getData()
|
|
106
119
|
[(vWriteIndex_ + i - fftSize_ + inputBuffer_->getSize()) %
|
|
107
120
|
inputBuffer_->getSize()];
|
|
108
121
|
}
|
|
109
122
|
}
|
|
110
123
|
|
|
111
|
-
void AnalyserNode::getByteTimeDomainData(uint8_t *data,
|
|
124
|
+
void AnalyserNode::getByteTimeDomainData(uint8_t *data, int length) {
|
|
112
125
|
auto size = std::min(fftSize_, length);
|
|
113
126
|
|
|
114
|
-
for (
|
|
127
|
+
for (int i = 0; i < size; i++) {
|
|
115
128
|
auto value = inputBuffer_->getData()
|
|
116
129
|
[(vWriteIndex_ + i - fftSize_ + inputBuffer_->getSize()) %
|
|
117
130
|
inputBuffer_->getSize()];
|
|
@@ -140,15 +153,11 @@ void AnalyserNode::processNode(
|
|
|
140
153
|
// Analyser should behave like a sniffer node, it should not modify the
|
|
141
154
|
// processingBus but instead copy the data to its own input buffer.
|
|
142
155
|
|
|
143
|
-
if (downMixBus_ == nullptr) {
|
|
144
|
-
downMixBus_ = std::make_unique<AudioBus>(
|
|
145
|
-
context_->getSampleRate(), processingBus->getSize(), 1);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
156
|
downMixBus_->copy(processingBus);
|
|
149
157
|
|
|
150
158
|
if (vWriteIndex_ + framesToProcess > inputBuffer_->getSize()) {
|
|
151
|
-
auto framesToCopy =
|
|
159
|
+
auto framesToCopy =
|
|
160
|
+
static_cast<int>(inputBuffer_->getSize()) - vWriteIndex_;
|
|
152
161
|
memcpy(
|
|
153
162
|
inputBuffer_->getData() + vWriteIndex_,
|
|
154
163
|
downMixBus_->getChannel(0)->getData(),
|
|
@@ -197,7 +206,14 @@ void AnalyserNode::doFFTAnalysis() {
|
|
|
197
206
|
tempBuffer.copy(inputBuffer_.get(), vWriteIndex_ - fftSize_, 0, fftSize_);
|
|
198
207
|
}
|
|
199
208
|
|
|
200
|
-
|
|
209
|
+
switch (windowType_) {
|
|
210
|
+
case WindowType::BLACKMAN:
|
|
211
|
+
AnalyserNode::applyBlackManWindow(tempBuffer.getData(), fftSize_);
|
|
212
|
+
break;
|
|
213
|
+
case WindowType::HANN:
|
|
214
|
+
AnalyserNode::applyHannWindow(tempBuffer.getData(), fftSize_);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
201
217
|
|
|
202
218
|
// do fft analysis - get frequency domain data
|
|
203
219
|
fftFrame_->doFFT(tempBuffer.getData());
|
|
@@ -211,7 +227,7 @@ void AnalyserNode::doFFTAnalysis() {
|
|
|
211
227
|
const float magnitudeScale = 1.0f / static_cast<float>(fftSize_);
|
|
212
228
|
auto magnitudeBufferData = magnitudeBuffer_->getData();
|
|
213
229
|
|
|
214
|
-
for (
|
|
230
|
+
for (int i = 0; i < magnitudeBuffer_->getSize(); i++) {
|
|
215
231
|
std::complex<float> c(realFFTFrameData[i], imaginaryFFTFrameData[i]);
|
|
216
232
|
auto scalarMagnitude = std::abs(c) * magnitudeScale;
|
|
217
233
|
magnitudeBufferData[i] = static_cast<float>(
|
|
@@ -220,17 +236,23 @@ void AnalyserNode::doFFTAnalysis() {
|
|
|
220
236
|
}
|
|
221
237
|
}
|
|
222
238
|
|
|
223
|
-
void AnalyserNode::
|
|
239
|
+
void AnalyserNode::applyBlackManWindow(float *data, int length) {
|
|
224
240
|
// https://www.sciencedirect.com/topics/engineering/blackman-window
|
|
225
|
-
|
|
226
|
-
auto a0 = 0.5f * (1 - alpha);
|
|
227
|
-
auto a1 = 0.5f;
|
|
228
|
-
auto a2 = 0.5f * alpha;
|
|
241
|
+
// https://docs.scipy.org/doc//scipy-1.2.3/reference/generated/scipy.signal.windows.blackman.html#scipy.signal.windows.blackman
|
|
229
242
|
|
|
230
243
|
for (int i = 0; i < length; ++i) {
|
|
231
244
|
auto x = static_cast<float>(i) / static_cast<float>(length);
|
|
232
|
-
auto window =
|
|
233
|
-
|
|
245
|
+
auto window = 0.42f - 0.5f * cos(2 * PI * x) + 0.08f * cos(4 * PI * x);
|
|
246
|
+
data[i] *= window;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
void AnalyserNode::applyHannWindow(float *data, int length) {
|
|
251
|
+
// https://www.sciencedirect.com/topics/engineering/hanning-window
|
|
252
|
+
// https://docs.scipy.org/doc//scipy-1.2.3/reference/generated/scipy.signal.windows.hann.html#scipy.signal.windows.hann
|
|
253
|
+
for (int i = 0; i < length; ++i) {
|
|
254
|
+
auto x = static_cast<float>(i) / static_cast<float>(length - 1);
|
|
255
|
+
auto window = 0.5f - 0.5f * cos(2 * PI * x);
|
|
234
256
|
data[i] *= window;
|
|
235
257
|
}
|
|
236
258
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <memory>
|
|
4
|
+
#include <cstddef>
|
|
5
|
+
#include <string>
|
|
4
6
|
|
|
5
7
|
#include "AudioNode.h"
|
|
6
8
|
|
|
@@ -12,32 +14,36 @@ class FFTFrame;
|
|
|
12
14
|
|
|
13
15
|
class AnalyserNode : public AudioNode {
|
|
14
16
|
public:
|
|
17
|
+
enum class WindowType { BLACKMAN, HANN };
|
|
15
18
|
explicit AnalyserNode(BaseAudioContext *context);
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
int getFftSize() const;
|
|
21
|
+
int getFrequencyBinCount() const;
|
|
22
|
+
float getMinDecibels() const;
|
|
23
|
+
float getMaxDecibels() const;
|
|
24
|
+
float getSmoothingTimeConstant() const;
|
|
25
|
+
std::string getWindowType() const;
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
void
|
|
24
|
-
void
|
|
25
|
-
void
|
|
26
|
-
void
|
|
27
|
+
void setFftSize(int fftSize);
|
|
28
|
+
void setMinDecibels(float minDecibels);
|
|
29
|
+
void setMaxDecibels(float maxDecibels);
|
|
30
|
+
void setSmoothingTimeConstant(float smoothingTimeConstant);
|
|
31
|
+
void setWindowType(const std::string &type);
|
|
27
32
|
|
|
28
|
-
void getFloatFrequencyData(float *data,
|
|
29
|
-
void getByteFrequencyData(uint8_t *data,
|
|
30
|
-
void getFloatTimeDomainData(float *data,
|
|
31
|
-
void getByteTimeDomainData(uint8_t *data,
|
|
33
|
+
void getFloatFrequencyData(float *data, int length);
|
|
34
|
+
void getByteFrequencyData(uint8_t *data, int length);
|
|
35
|
+
void getFloatTimeDomainData(float *data, int length);
|
|
36
|
+
void getByteTimeDomainData(uint8_t *data, int length);
|
|
32
37
|
|
|
33
38
|
protected:
|
|
34
39
|
void processNode(AudioBus *processingBus, int framesToProcess) override;
|
|
35
40
|
|
|
36
41
|
private:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
int fftSize_;
|
|
43
|
+
float minDecibels_;
|
|
44
|
+
float maxDecibels_;
|
|
45
|
+
float smoothingTimeConstant_;
|
|
46
|
+
WindowType windowType_;
|
|
41
47
|
|
|
42
48
|
std::unique_ptr<AudioArray> inputBuffer_;
|
|
43
49
|
std::unique_ptr<AudioBus> downMixBus_;
|
|
@@ -47,8 +53,34 @@ class AnalyserNode : public AudioNode {
|
|
|
47
53
|
std::unique_ptr<AudioArray> magnitudeBuffer_;
|
|
48
54
|
bool shouldDoFFTAnalysis_ { true };
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
static WindowType fromString(const std::string &type) {
|
|
57
|
+
std::string lowerType = type;
|
|
58
|
+
std::transform(
|
|
59
|
+
lowerType.begin(), lowerType.end(), lowerType.begin(), ::tolower);
|
|
60
|
+
if (lowerType == "blackman") {
|
|
61
|
+
return WindowType::BLACKMAN;
|
|
62
|
+
}
|
|
63
|
+
if (lowerType == "hann") {
|
|
64
|
+
return WindowType::HANN;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
throw std::invalid_argument("Unknown window type");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static std::string toString(WindowType type) {
|
|
71
|
+
switch (type) {
|
|
72
|
+
case WindowType::BLACKMAN:
|
|
73
|
+
return "blackman";
|
|
74
|
+
case WindowType::HANN:
|
|
75
|
+
return "hann";
|
|
76
|
+
default:
|
|
77
|
+
throw std::invalid_argument("Unknown window type");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
void doFFTAnalysis();
|
|
82
|
+
static void applyBlackManWindow(float *data, int length);
|
|
83
|
+
static void applyHannWindow(float *data, int length);
|
|
52
84
|
};
|
|
53
85
|
|
|
54
86
|
} // namespace audioapi
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
namespace audioapi {
|
|
7
7
|
|
|
8
|
-
AudioArray::AudioArray(
|
|
8
|
+
AudioArray::AudioArray(size_t size) : data_(nullptr), size_(size) {
|
|
9
9
|
resize(size);
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -16,7 +16,7 @@ AudioArray::~AudioArray() {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
size_t AudioArray::getSize() const {
|
|
20
20
|
return size_;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -24,11 +24,11 @@ float *AudioArray::getData() const {
|
|
|
24
24
|
return data_;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
float &AudioArray::operator[](
|
|
27
|
+
float &AudioArray::operator[](size_t index) {
|
|
28
28
|
return data_[index];
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const float &AudioArray::operator[](
|
|
31
|
+
const float &AudioArray::operator[](size_t index) const {
|
|
32
32
|
return data_[index];
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -42,7 +42,7 @@ void AudioArray::normalize() {
|
|
|
42
42
|
VectorMath::multiplyByScalar(data_, 1.0f / maxAbsValue, data_, size_);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
void AudioArray::resize(
|
|
45
|
+
void AudioArray::resize(size_t size) {
|
|
46
46
|
if (size == size_) {
|
|
47
47
|
if (!data_) {
|
|
48
48
|
data_ = new float[size];
|
|
@@ -71,7 +71,7 @@ void AudioArray::zero() {
|
|
|
71
71
|
zero(0, size_);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
void AudioArray::zero(
|
|
74
|
+
void AudioArray::zero(size_t start, size_t length) {
|
|
75
75
|
memset(data_ + start, 0, length * sizeof(float));
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -79,15 +79,15 @@ void AudioArray::sum(const AudioArray *source) {
|
|
|
79
79
|
sum(source, 0, 0, size_);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
void AudioArray::sum(const AudioArray *source,
|
|
82
|
+
void AudioArray::sum(const AudioArray *source, size_t start, size_t length) {
|
|
83
83
|
sum(source, start, start, length);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
void AudioArray::sum(
|
|
87
87
|
const AudioArray *source,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
size_t sourceStart,
|
|
89
|
+
size_t destinationStart,
|
|
90
|
+
size_t length) {
|
|
91
91
|
VectorMath::add(
|
|
92
92
|
data_ + destinationStart,
|
|
93
93
|
source->getData() + sourceStart,
|
|
@@ -99,15 +99,15 @@ void AudioArray::copy(const AudioArray *source) {
|
|
|
99
99
|
copy(source, 0, size_);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
void AudioArray::copy(const AudioArray *source,
|
|
102
|
+
void AudioArray::copy(const AudioArray *source, size_t start, size_t length) {
|
|
103
103
|
copy(source, start, start, length);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
void AudioArray::copy(
|
|
107
107
|
const AudioArray *source,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
size_t sourceStart,
|
|
109
|
+
size_t destinationStart,
|
|
110
|
+
size_t length) {
|
|
111
111
|
memcpy(
|
|
112
112
|
data_ + destinationStart,
|
|
113
113
|
source->getData() + sourceStart,
|