react-native-audio-api 0.9.0-nightly-7ecb495-20251008 → 0.9.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/src/main/cpp/audioapi/android/core/{utils/AudioDecoder.cpp → AudioDecoder.cpp} +75 -79
- package/android/src/main/jniLibs/arm64-v8a/libavcodec.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libavformat.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libavutil.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libswresample.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libavcodec.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libavformat.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libavutil.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libswresample.so +0 -0
- package/android/src/main/jniLibs/x86/libavcodec.so +0 -0
- package/android/src/main/jniLibs/x86/libavformat.so +0 -0
- package/android/src/main/jniLibs/x86/libavutil.so +0 -0
- package/android/src/main/jniLibs/x86/libswresample.so +0 -0
- package/android/src/main/jniLibs/x86_64/libavcodec.so +0 -0
- package/android/src/main/jniLibs/x86_64/libavformat.so +0 -0
- package/android/src/main/jniLibs/x86_64/libavutil.so +0 -0
- package/android/src/main/jniLibs/x86_64/libswresample.so +0 -0
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +43 -124
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +101 -1
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +3 -0
- package/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp +3 -8
- package/common/cpp/audioapi/core/AudioContext.cpp +2 -0
- package/common/cpp/audioapi/core/BaseAudioContext.cpp +35 -0
- package/common/cpp/audioapi/core/BaseAudioContext.h +12 -4
- package/common/cpp/audioapi/core/OfflineAudioContext.cpp +2 -0
- package/common/cpp/audioapi/core/effects/WorkletNode.cpp +16 -28
- package/common/cpp/audioapi/core/effects/WorkletNode.h +2 -3
- package/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +5 -6
- package/common/cpp/audioapi/core/sources/AudioBuffer.h +1 -0
- package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +0 -4
- package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +0 -1
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +0 -2
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +0 -4
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +0 -1
- package/common/cpp/audioapi/core/sources/StreamerNode.cpp +16 -6
- package/common/cpp/audioapi/core/sources/StreamerNode.h +3 -1
- package/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +2 -3
- package/common/cpp/audioapi/core/utils/AudioDecoder.h +91 -36
- package/common/cpp/audioapi/core/utils/Constants.h +0 -4
- package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +5 -1
- package/common/cpp/audioapi/external/libavcodec.xcframework/ios-arm64/libavcodec.framework/libavcodec +0 -0
- package/common/cpp/audioapi/external/libavcodec.xcframework/ios-arm64_x86_64-simulator/libavcodec.framework/libavcodec +0 -0
- package/common/cpp/audioapi/external/libavformat.xcframework/Info.plist +5 -5
- package/common/cpp/audioapi/external/libavformat.xcframework/ios-arm64/libavformat.framework/libavformat +0 -0
- package/common/cpp/audioapi/external/libavformat.xcframework/ios-arm64_x86_64-simulator/libavformat.framework/libavformat +0 -0
- package/common/cpp/audioapi/external/libavutil.xcframework/ios-arm64/libavutil.framework/libavutil +0 -0
- package/common/cpp/audioapi/external/libavutil.xcframework/ios-arm64_x86_64-simulator/libavutil.framework/libavutil +0 -0
- package/common/cpp/audioapi/external/libswresample.xcframework/Info.plist +5 -5
- package/common/cpp/audioapi/external/libswresample.xcframework/ios-arm64/libswresample.framework/libswresample +0 -0
- package/common/cpp/audioapi/external/libswresample.xcframework/ios-arm64_x86_64-simulator/libswresample.framework/libswresample +0 -0
- package/common/cpp/audioapi/jsi/AudioArrayBuffer.cpp +2 -2
- package/common/cpp/audioapi/jsi/AudioArrayBuffer.h +10 -11
- package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +282 -241
- package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +19 -57
- package/common/cpp/audioapi/libs/ffmpeg/ffmpeg_setup.sh +1 -1
- package/common/cpp/audioapi/utils/AudioBus.cpp +4 -0
- package/common/cpp/audioapi/utils/AudioBus.h +1 -0
- package/common/cpp/test/CMakeLists.txt +8 -5
- package/common/cpp/test/RunTests.sh +2 -2
- package/common/cpp/test/{AudioParamTest.cpp → src/AudioParamTest.cpp} +1 -1
- package/common/cpp/test/src/ConstantSourceTest.cpp +64 -0
- package/common/cpp/test/{GainTest.cpp → src/GainTest.cpp} +11 -10
- package/common/cpp/test/{MockAudioEventHandlerRegistry.h → src/MockAudioEventHandlerRegistry.h} +4 -2
- package/common/cpp/test/{OscillatorTest.cpp → src/OscillatorTest.cpp} +6 -4
- package/common/cpp/test/{StereoPannerTest.cpp → src/StereoPannerTest.cpp} +1 -1
- package/ios/audioapi/ios/core/AudioDecoder.mm +156 -0
- package/lib/commonjs/api.js +1 -21
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/core/BaseAudioContext.js +18 -11
- package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
- package/lib/module/api.js +1 -3
- package/lib/module/api.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +18 -11
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/typescript/api.d.ts +1 -5
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/core/BaseAudioContext.d.ts +6 -3
- package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +3 -10
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api.ts +0 -10
- package/src/core/BaseAudioContext.ts +29 -26
- package/src/interfaces.ts +6 -26
- package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.cpp +0 -133
- package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.h +0 -28
- package/common/cpp/audioapi/HostObjects/utils/AudioStretcherHostObject.cpp +0 -58
- package/common/cpp/audioapi/HostObjects/utils/AudioStretcherHostObject.h +0 -26
- package/common/cpp/audioapi/core/types/AudioFormat.h +0 -16
- package/common/cpp/audioapi/core/utils/AudioStretcher.cpp +0 -75
- package/common/cpp/audioapi/core/utils/AudioStretcher.h +0 -30
- package/ios/audioapi/ios/core/utils/AudioDecoder.mm +0 -160
- package/lib/commonjs/core/AudioDecoder.js +0 -48
- package/lib/commonjs/core/AudioDecoder.js.map +0 -1
- package/lib/commonjs/core/AudioStretcher.js +0 -31
- package/lib/commonjs/core/AudioStretcher.js.map +0 -1
- package/lib/module/core/AudioDecoder.js +0 -42
- package/lib/module/core/AudioDecoder.js.map +0 -1
- package/lib/module/core/AudioStretcher.js +0 -26
- package/lib/module/core/AudioStretcher.js.map +0 -1
- package/lib/typescript/core/AudioDecoder.d.ts +0 -4
- package/lib/typescript/core/AudioDecoder.d.ts.map +0 -1
- package/lib/typescript/core/AudioStretcher.d.ts +0 -3
- package/lib/typescript/core/AudioStretcher.d.ts.map +0 -1
- package/src/core/AudioDecoder.ts +0 -78
- package/src/core/AudioStretcher.ts +0 -43
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
|
|
2
2
|
|
|
3
3
|
#include <audioapi/jsi/AudioArrayBuffer.h>
|
|
4
|
+
#include <audioapi/utils/AudioBus.h>
|
|
4
5
|
|
|
5
6
|
namespace audioapi {
|
|
6
7
|
|
|
@@ -42,14 +43,8 @@ JSI_PROPERTY_GETTER_IMPL(AudioBufferHostObject, numberOfChannels) {
|
|
|
42
43
|
|
|
43
44
|
JSI_HOST_FUNCTION_IMPL(AudioBufferHostObject, getChannelData) {
|
|
44
45
|
auto channel = static_cast<int>(args[0].getNumber());
|
|
45
|
-
auto
|
|
46
|
-
|
|
47
|
-
auto length = static_cast<int>(audioBuffer_->getLength());
|
|
48
|
-
auto size = static_cast<int>(length * sizeof(float));
|
|
49
|
-
|
|
50
|
-
// reading or writing from this ArrayBuffer could cause a crash
|
|
51
|
-
// if underlying channelData is deallocated
|
|
52
|
-
auto audioArrayBuffer = std::make_shared<AudioArrayBuffer>(channelData, size);
|
|
46
|
+
auto audioArrayBuffer = std::make_shared<AudioArrayBuffer>(
|
|
47
|
+
audioBuffer_->bus_->getSharedChannel(channel));
|
|
53
48
|
auto arrayBuffer = jsi::ArrayBuffer(runtime, audioArrayBuffer);
|
|
54
49
|
|
|
55
50
|
auto float32ArrayCtor =
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
#include <audioapi/core/AudioContext.h>
|
|
8
8
|
#include <audioapi/core/destinations/AudioDestinationNode.h>
|
|
9
|
+
#include <audioapi/core/utils/AudioDecoder.h>
|
|
9
10
|
#include <audioapi/core/utils/AudioNodeManager.h>
|
|
10
11
|
|
|
11
12
|
namespace audioapi {
|
|
@@ -25,6 +26,7 @@ AudioContext::AudioContext(
|
|
|
25
26
|
#endif
|
|
26
27
|
|
|
27
28
|
sampleRate_ = sampleRate;
|
|
29
|
+
audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate);
|
|
28
30
|
|
|
29
31
|
if (initSuspended) {
|
|
30
32
|
playerHasBeenStarted_ = false;
|
|
@@ -176,6 +176,41 @@ std::shared_ptr<AnalyserNode> BaseAudioContext::createAnalyser() {
|
|
|
176
176
|
return analyser;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
std::shared_ptr<AudioBuffer> BaseAudioContext::decodeAudioDataSource(
|
|
180
|
+
const std::string &path) {
|
|
181
|
+
auto audioBus = audioDecoder_->decodeWithFilePath(path);
|
|
182
|
+
|
|
183
|
+
if (!audioBus) {
|
|
184
|
+
return nullptr;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return std::make_shared<AudioBuffer>(audioBus);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
std::shared_ptr<AudioBuffer> BaseAudioContext::decodeAudioData(
|
|
191
|
+
const void *data,
|
|
192
|
+
size_t size) {
|
|
193
|
+
auto audioBus = audioDecoder_->decodeWithMemoryBlock(data, size);
|
|
194
|
+
|
|
195
|
+
if (!audioBus) {
|
|
196
|
+
return nullptr;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return std::make_shared<AudioBuffer>(audioBus);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
std::shared_ptr<AudioBuffer> BaseAudioContext::decodeWithPCMInBase64(
|
|
203
|
+
const std::string &data,
|
|
204
|
+
float playbackSpeed) {
|
|
205
|
+
auto audioBus = audioDecoder_->decodeWithPCMInBase64(data, playbackSpeed);
|
|
206
|
+
|
|
207
|
+
if (!audioBus) {
|
|
208
|
+
return nullptr;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return std::make_shared<AudioBuffer>(audioBus);
|
|
212
|
+
}
|
|
213
|
+
|
|
179
214
|
AudioNodeManager *BaseAudioContext::getNodeManager() {
|
|
180
215
|
return nodeManager_.get();
|
|
181
216
|
}
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
#include <audioapi/core/types/ContextState.h>
|
|
4
4
|
#include <audioapi/core/types/OscillatorType.h>
|
|
5
5
|
#include <audioapi/core/utils/worklets/SafeIncludes.h>
|
|
6
|
-
|
|
7
|
-
#include <complex>
|
|
8
|
-
#include <cstddef>
|
|
6
|
+
|
|
9
7
|
#include <functional>
|
|
10
8
|
#include <memory>
|
|
11
9
|
#include <string>
|
|
12
10
|
#include <utility>
|
|
13
11
|
#include <vector>
|
|
12
|
+
#include <complex>
|
|
13
|
+
#include <cstddef>
|
|
14
|
+
#include <cassert>
|
|
14
15
|
|
|
15
16
|
namespace audioapi {
|
|
16
17
|
|
|
@@ -26,6 +27,7 @@ class BiquadFilterNode;
|
|
|
26
27
|
class AudioDestinationNode;
|
|
27
28
|
class AudioBufferSourceNode;
|
|
28
29
|
class AudioBufferQueueSourceNode;
|
|
30
|
+
class AudioDecoder;
|
|
29
31
|
class AnalyserNode;
|
|
30
32
|
class AudioEventHandlerRegistry;
|
|
31
33
|
class IAudioEventHandlerRegistry;
|
|
@@ -66,6 +68,10 @@ class BaseAudioContext {
|
|
|
66
68
|
int length);
|
|
67
69
|
std::shared_ptr<AnalyserNode> createAnalyser();
|
|
68
70
|
|
|
71
|
+
std::shared_ptr<AudioBuffer> decodeAudioDataSource(const std::string &path);
|
|
72
|
+
std::shared_ptr<AudioBuffer> decodeAudioData(const void *data, size_t size);
|
|
73
|
+
std::shared_ptr<AudioBuffer> decodeWithPCMInBase64(const std::string &data, float playbackSpeed);
|
|
74
|
+
|
|
69
75
|
std::shared_ptr<PeriodicWave> getBasicWaveForm(OscillatorType type);
|
|
70
76
|
[[nodiscard]] float getNyquistFrequency() const;
|
|
71
77
|
AudioNodeManager *getNodeManager();
|
|
@@ -79,7 +85,9 @@ class BaseAudioContext {
|
|
|
79
85
|
|
|
80
86
|
std::shared_ptr<AudioDestinationNode> destination_;
|
|
81
87
|
// init in AudioContext or OfflineContext constructor
|
|
82
|
-
|
|
88
|
+
std::shared_ptr<AudioDecoder> audioDecoder_ {};
|
|
89
|
+
// init in AudioContext or OfflineContext constructor
|
|
90
|
+
float sampleRate_ {};
|
|
83
91
|
ContextState state_ = ContextState::RUNNING;
|
|
84
92
|
std::shared_ptr<AudioNodeManager> nodeManager_;
|
|
85
93
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <audioapi/core/AudioContext.h>
|
|
4
4
|
#include <audioapi/core/destinations/AudioDestinationNode.h>
|
|
5
5
|
#include <audioapi/core/sources/AudioBuffer.h>
|
|
6
|
+
#include <audioapi/core/utils/AudioDecoder.h>
|
|
6
7
|
#include <audioapi/core/utils/AudioNodeManager.h>
|
|
7
8
|
#include <audioapi/core/utils/Constants.h>
|
|
8
9
|
#include <audioapi/core/utils/Locker.h>
|
|
@@ -29,6 +30,7 @@ OfflineAudioContext::OfflineAudioContext(
|
|
|
29
30
|
numberOfChannels_(numberOfChannels),
|
|
30
31
|
currentSampleFrame_(0) {
|
|
31
32
|
sampleRate_ = sampleRate;
|
|
33
|
+
audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate_);
|
|
32
34
|
resultBus_ = std::make_shared<AudioBus>(
|
|
33
35
|
static_cast<int>(length_), numberOfChannels_, sampleRate_);
|
|
34
36
|
}
|
|
@@ -9,25 +9,16 @@ WorkletNode::WorkletNode(
|
|
|
9
9
|
size_t bufferLength,
|
|
10
10
|
size_t inputChannelCount)
|
|
11
11
|
: AudioNode(context),
|
|
12
|
-
buffRealLength_(bufferLength * sizeof(float)),
|
|
13
|
-
bufferLength_(bufferLength),
|
|
14
12
|
workletRunner_(runtime),
|
|
15
13
|
shareableWorklet_(worklet),
|
|
14
|
+
bufferLength_(bufferLength),
|
|
16
15
|
inputChannelCount_(inputChannelCount),
|
|
17
16
|
curBuffIndex_(0) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
buffs_.emplace_back(new uint8_t[buffRealLength_]);
|
|
21
|
-
}
|
|
17
|
+
bus_ = std::make_shared<AudioBus>(
|
|
18
|
+
bufferLength, inputChannelCount, context->getSampleRate());
|
|
22
19
|
isInitialized_ = true;
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
WorkletNode::~WorkletNode() {
|
|
26
|
-
for (auto &buff : buffs_) {
|
|
27
|
-
delete[] buff;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
22
|
std::shared_ptr<AudioBus> WorkletNode::processNode(
|
|
32
23
|
const std::shared_ptr<AudioBus> &processingBus,
|
|
33
24
|
int framesToProcess) {
|
|
@@ -40,17 +31,11 @@ std::shared_ptr<AudioBus> WorkletNode::processNode(
|
|
|
40
31
|
size_t needsToProcess = framesToProcess - processed;
|
|
41
32
|
size_t shouldProcess = std::min(framesToWorkletInvoke, needsToProcess);
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
auto channelData = processingBus->getChannel(ch)->getData();
|
|
49
|
-
std::memcpy(
|
|
50
|
-
/* dest */ buffs_[ch] + curBuffIndex_ * sizeof(float),
|
|
51
|
-
/* src */ reinterpret_cast<const uint8_t *>(channelData + processed),
|
|
52
|
-
/* size */ shouldProcess * sizeof(float));
|
|
53
|
-
}
|
|
34
|
+
/// here we copy
|
|
35
|
+
/// to [curBuffIndex_, curBuffIndex_ + shouldProcess]
|
|
36
|
+
/// from [processed, processed + shouldProcess]
|
|
37
|
+
bus_->copy(processingBus.get(), processed, curBuffIndex_, shouldProcess);
|
|
38
|
+
|
|
54
39
|
processed += shouldProcess;
|
|
55
40
|
curBuffIndex_ += shouldProcess;
|
|
56
41
|
|
|
@@ -63,16 +48,19 @@ std::shared_ptr<AudioBus> WorkletNode::processNode(
|
|
|
63
48
|
/// Arguments preparation
|
|
64
49
|
auto jsArray = jsi::Array(uiRuntimeRaw, channelCount_);
|
|
65
50
|
for (size_t ch = 0; ch < channelCount_; ch++) {
|
|
66
|
-
|
|
67
|
-
|
|
51
|
+
auto audioArray = std::make_shared<AudioArray>(bufferLength_);
|
|
52
|
+
audioArray->copy(bus_->getChannel(ch));
|
|
68
53
|
auto sharedAudioArray =
|
|
69
|
-
std::make_shared<AudioArrayBuffer>(
|
|
54
|
+
std::make_shared<AudioArrayBuffer>(audioArray);
|
|
55
|
+
auto sharedAudioArraySize = sharedAudioArray->size();
|
|
70
56
|
auto arrayBuffer =
|
|
71
57
|
jsi::ArrayBuffer(uiRuntimeRaw, std::move(sharedAudioArray));
|
|
58
|
+
arrayBuffer.setExternalMemoryPressure(
|
|
59
|
+
uiRuntimeRaw, sharedAudioArraySize);
|
|
72
60
|
jsArray.setValueAtIndex(uiRuntimeRaw, ch, std::move(arrayBuffer));
|
|
73
61
|
}
|
|
74
|
-
|
|
75
|
-
|
|
62
|
+
|
|
63
|
+
bus_->zero();
|
|
76
64
|
|
|
77
65
|
workletRunner_.executeWorklet(
|
|
78
66
|
shareableWorklet_,
|
|
@@ -42,7 +42,7 @@ class WorkletNode : public AudioNode {
|
|
|
42
42
|
size_t inputChannelCount
|
|
43
43
|
);
|
|
44
44
|
|
|
45
|
-
~WorkletNode() override;
|
|
45
|
+
~WorkletNode() override = default;
|
|
46
46
|
|
|
47
47
|
protected:
|
|
48
48
|
std::shared_ptr<AudioBus> processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
|
|
@@ -51,10 +51,9 @@ class WorkletNode : public AudioNode {
|
|
|
51
51
|
private:
|
|
52
52
|
WorkletsRunner workletRunner_;
|
|
53
53
|
std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
|
|
54
|
-
std::
|
|
54
|
+
std::shared_ptr<AudioBus> bus_;
|
|
55
55
|
|
|
56
56
|
/// @brief Length of the byte buffer that will be passed to the AudioArrayBuffer
|
|
57
|
-
size_t buffRealLength_;
|
|
58
57
|
size_t bufferLength_;
|
|
59
58
|
size_t inputChannelCount_;
|
|
60
59
|
size_t curBuffIndex_;
|
|
@@ -16,13 +16,12 @@ WorkletProcessingNode::WorkletProcessingNode(
|
|
|
16
16
|
outputBuffsHandles_.resize(maxChannelCount);
|
|
17
17
|
|
|
18
18
|
for (size_t i = 0; i < maxChannelCount; ++i) {
|
|
19
|
-
auto
|
|
20
|
-
inputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(
|
|
21
|
-
inputBuff, RENDER_QUANTUM_SIZE * sizeof(float));
|
|
19
|
+
auto inputAudioArray = std::make_shared<AudioArray>(RENDER_QUANTUM_SIZE);
|
|
20
|
+
inputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(inputAudioArray);
|
|
22
21
|
|
|
23
|
-
auto
|
|
24
|
-
outputBuffsHandles_[i] =
|
|
25
|
-
|
|
22
|
+
auto outputAudioArray = std::make_shared<AudioArray>(RENDER_QUANTUM_SIZE);
|
|
23
|
+
outputBuffsHandles_[i] =
|
|
24
|
+
std::make_shared<AudioArrayBuffer>(outputAudioArray);
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
27
|
|
|
@@ -27,10 +27,6 @@ AudioBufferBaseSourceNode::AudioBufferBaseSourceNode(
|
|
|
27
27
|
std::make_shared<signalsmith::stretch::SignalsmithStretch<float>>();
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
AudioBufferBaseSourceNode::~AudioBufferBaseSourceNode() {
|
|
31
|
-
clearOnPositionChangedCallback();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
30
|
std::shared_ptr<AudioParam> AudioBufferBaseSourceNode::getDetuneParam() const {
|
|
35
31
|
return detuneParam_;
|
|
36
32
|
}
|
|
@@ -15,7 +15,6 @@ class AudioParam;
|
|
|
15
15
|
class AudioBufferBaseSourceNode : public AudioScheduledSourceNode {
|
|
16
16
|
public:
|
|
17
17
|
explicit AudioBufferBaseSourceNode(BaseAudioContext *context, bool pitchCorrection);
|
|
18
|
-
~AudioBufferBaseSourceNode() override;
|
|
19
18
|
|
|
20
19
|
[[nodiscard]] std::shared_ptr<AudioParam> getDetuneParam() const;
|
|
21
20
|
[[nodiscard]] std::shared_ptr<AudioParam> getPlaybackRateParam() const;
|
|
@@ -16,10 +16,6 @@ AudioScheduledSourceNode::AudioScheduledSourceNode(BaseAudioContext *context)
|
|
|
16
16
|
numberOfInputs_ = 0;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
AudioScheduledSourceNode::~AudioScheduledSourceNode() {
|
|
20
|
-
clearOnEndedCallback();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
19
|
void AudioScheduledSourceNode::start(double when) {
|
|
24
20
|
playbackState_ = PlaybackState::SCHEDULED;
|
|
25
21
|
startTime_ = when;
|
|
@@ -27,7 +27,6 @@ class AudioScheduledSourceNode : public AudioNode {
|
|
|
27
27
|
// FINISHED: The node has finished playing.
|
|
28
28
|
enum class PlaybackState { UNSCHEDULED, SCHEDULED, PLAYING, STOP_SCHEDULED, FINISHED };
|
|
29
29
|
explicit AudioScheduledSourceNode(BaseAudioContext *context);
|
|
30
|
-
~AudioScheduledSourceNode() override;
|
|
31
30
|
|
|
32
31
|
void start(double when);
|
|
33
32
|
virtual void stop(double when);
|
|
@@ -44,10 +44,14 @@ bool StreamerNode::initialize(const std::string &input_url) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (!openInput(input_url)) {
|
|
47
|
+
if (VERBOSE)
|
|
48
|
+
printf("Failed to open input\n");
|
|
47
49
|
return false;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
if (!findAudioStream() || !setupDecoder() || !setupResampler()) {
|
|
53
|
+
if (VERBOSE)
|
|
54
|
+
printf("Failed to find/setup audio stream\n");
|
|
51
55
|
cleanup();
|
|
52
56
|
return false;
|
|
53
57
|
}
|
|
@@ -56,6 +60,8 @@ bool StreamerNode::initialize(const std::string &input_url) {
|
|
|
56
60
|
frame_ = av_frame_alloc();
|
|
57
61
|
|
|
58
62
|
if (pkt_ == nullptr || frame_ == nullptr) {
|
|
63
|
+
if (VERBOSE)
|
|
64
|
+
printf("Failed to allocate packet or frame\n");
|
|
59
65
|
cleanup();
|
|
60
66
|
return false;
|
|
61
67
|
}
|
|
@@ -119,29 +125,24 @@ void StreamerNode::streamAudio() {
|
|
|
119
125
|
while (streamFlag.load()) {
|
|
120
126
|
if (pendingFrame_ != nullptr) {
|
|
121
127
|
if (!processFrameWithResampler(pendingFrame_)) {
|
|
122
|
-
cleanup();
|
|
123
128
|
return;
|
|
124
129
|
}
|
|
125
130
|
} else {
|
|
126
131
|
if (av_read_frame(fmtCtx_, pkt_) < 0) {
|
|
127
|
-
cleanup();
|
|
128
132
|
return;
|
|
129
133
|
}
|
|
130
134
|
if (pkt_->stream_index == audio_stream_index_) {
|
|
131
135
|
if (avcodec_send_packet(codecCtx_, pkt_) != 0) {
|
|
132
|
-
cleanup();
|
|
133
136
|
return;
|
|
134
137
|
}
|
|
135
138
|
if (avcodec_receive_frame(codecCtx_, frame_) != 0) {
|
|
136
|
-
cleanup();
|
|
137
139
|
return;
|
|
138
140
|
}
|
|
139
141
|
if (!processFrameWithResampler(frame_)) {
|
|
140
|
-
cleanup();
|
|
141
142
|
return;
|
|
142
143
|
}
|
|
143
|
-
av_packet_unref(pkt_);
|
|
144
144
|
}
|
|
145
|
+
av_packet_unref(pkt_);
|
|
145
146
|
}
|
|
146
147
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
147
148
|
}
|
|
@@ -174,6 +175,13 @@ std::shared_ptr<AudioBus> StreamerNode::processNode(
|
|
|
174
175
|
(maxBufferSize_ - offsetLength) * sizeof(float));
|
|
175
176
|
}
|
|
176
177
|
bufferedBusIndex_ -= offsetLength;
|
|
178
|
+
} else {
|
|
179
|
+
if (VERBOSE)
|
|
180
|
+
printf(
|
|
181
|
+
"Buffer underrun: have %zu, need %zu\n",
|
|
182
|
+
bufferedBusIndex_,
|
|
183
|
+
(size_t)framesToProcess);
|
|
184
|
+
processingBus->zero();
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
return processingBus;
|
|
@@ -273,6 +281,8 @@ bool StreamerNode::setupDecoder() {
|
|
|
273
281
|
|
|
274
282
|
void StreamerNode::cleanup() {
|
|
275
283
|
streamFlag.store(false);
|
|
284
|
+
// cleanup cannot be called from the streaming thread so there is no need to
|
|
285
|
+
// check if we are in the same thread
|
|
276
286
|
streamingThread_.join();
|
|
277
287
|
if (swrCtx_ != nullptr) {
|
|
278
288
|
swr_free(&swrCtx_);
|
|
@@ -28,6 +28,8 @@ extern "C" {
|
|
|
28
28
|
#include <string>
|
|
29
29
|
#include <atomic>
|
|
30
30
|
|
|
31
|
+
static bool constexpr VERBOSE = false;
|
|
32
|
+
|
|
31
33
|
namespace audioapi {
|
|
32
34
|
|
|
33
35
|
class AudioBus;
|
|
@@ -94,7 +96,7 @@ class StreamerNode : public AudioScheduledSourceNode {
|
|
|
94
96
|
|
|
95
97
|
/**
|
|
96
98
|
* @brief Open the input stream
|
|
97
|
-
* @param
|
|
99
|
+
* @param inputUrl The URL of the input stream
|
|
98
100
|
* @return true if successful, false otherwise
|
|
99
101
|
* @note This function initializes the FFmpeg libraries and opens the input stream
|
|
100
102
|
*/
|
|
@@ -16,9 +16,8 @@ WorkletSourceNode::WorkletSourceNode(
|
|
|
16
16
|
size_t outputChannelCount = this->getChannelCount();
|
|
17
17
|
outputBuffsHandles_.resize(outputChannelCount);
|
|
18
18
|
for (size_t i = 0; i < outputChannelCount; ++i) {
|
|
19
|
-
auto
|
|
20
|
-
outputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(
|
|
21
|
-
buff, RENDER_QUANTUM_SIZE * sizeof(float));
|
|
19
|
+
auto audioArray = std::make_shared<AudioArray>(RENDER_QUANTUM_SIZE);
|
|
20
|
+
outputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(audioArray);
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
23
|
|
|
@@ -1,67 +1,124 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
#include <audioapi/
|
|
3
|
+
#include <audioapi/libs/audio-stretch/stretch.h>
|
|
4
4
|
#include <audioapi/libs/miniaudio/miniaudio.h>
|
|
5
|
-
#include <algorithm>
|
|
6
|
-
#include <cstring>
|
|
7
5
|
#include <memory>
|
|
8
6
|
#include <string>
|
|
9
7
|
#include <vector>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
#include <algorithm>
|
|
10
10
|
|
|
11
11
|
namespace audioapi {
|
|
12
12
|
|
|
13
|
-
class
|
|
13
|
+
enum class AudioFormat {
|
|
14
|
+
UNKNOWN,
|
|
15
|
+
WAV,
|
|
16
|
+
OGG,
|
|
17
|
+
FLAC,
|
|
18
|
+
AAC,
|
|
19
|
+
MP3,
|
|
20
|
+
M4A,
|
|
21
|
+
MP4,
|
|
22
|
+
MOV
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
class AudioBus;
|
|
14
26
|
|
|
15
27
|
static constexpr int CHUNK_SIZE = 4096;
|
|
16
28
|
|
|
17
29
|
class AudioDecoder {
|
|
18
30
|
public:
|
|
19
|
-
AudioDecoder()
|
|
31
|
+
explicit AudioDecoder(float sampleRate) : sampleRate_(sampleRate) {}
|
|
20
32
|
|
|
21
|
-
[[nodiscard]]
|
|
22
|
-
|
|
23
|
-
decodeWithMemoryBlock(
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
[[nodiscard]] std::shared_ptr<AudioBus> decodeWithFilePath(
|
|
34
|
+
const std::string &path) const;
|
|
35
|
+
[[nodiscard]] std::shared_ptr<AudioBus> decodeWithMemoryBlock(
|
|
36
|
+
const void *data,
|
|
37
|
+
size_t size) const;
|
|
38
|
+
[[nodiscard]] std::shared_ptr<AudioBus> decodeWithPCMInBase64(
|
|
39
|
+
const std::string &data,
|
|
40
|
+
float playbackSpeed) const;
|
|
26
41
|
|
|
27
42
|
private:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
float sampleRate_;
|
|
44
|
+
int numChannels_ = 2;
|
|
45
|
+
|
|
46
|
+
static std::vector<int16_t> readAllPcmFrames(
|
|
47
|
+
ma_decoder &decoder,
|
|
48
|
+
int numChannels,
|
|
49
|
+
ma_uint64 &outFramesRead);
|
|
50
|
+
static std::shared_ptr<AudioBus> makeAudioBusFromInt16Buffer(
|
|
51
|
+
const std::vector<int16_t> &buffer,
|
|
52
|
+
int numChannels,
|
|
53
|
+
float sampleRate);
|
|
54
|
+
|
|
55
|
+
void changePlaybackSpeedIfNeeded(
|
|
56
|
+
std::vector<int16_t> &buffer,
|
|
57
|
+
size_t framesDecoded,
|
|
58
|
+
int numChannels,
|
|
59
|
+
float playbackSpeed) const {
|
|
60
|
+
if (playbackSpeed == 1.0f) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
auto stretcher = stretch_init(
|
|
65
|
+
static_cast<int>(sampleRate_ / 333.0f),
|
|
66
|
+
static_cast<int>(sampleRate_ / 55.0f),
|
|
67
|
+
numChannels,
|
|
68
|
+
0x1);
|
|
31
69
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
70
|
+
int maxOutputFrames = stretch_output_capacity(
|
|
71
|
+
stretcher, static_cast<int>(framesDecoded), 1 / playbackSpeed);
|
|
72
|
+
std::vector<int16_t> stretchedBuffer(maxOutputFrames);
|
|
73
|
+
|
|
74
|
+
int outputFrames = stretch_samples(
|
|
75
|
+
stretcher,
|
|
76
|
+
buffer.data(),
|
|
77
|
+
static_cast<int>(framesDecoded),
|
|
78
|
+
stretchedBuffer.data(),
|
|
79
|
+
1 / playbackSpeed);
|
|
80
|
+
|
|
81
|
+
outputFrames +=
|
|
82
|
+
stretch_flush(stretcher, stretchedBuffer.data() + (outputFrames));
|
|
83
|
+
stretchedBuffer.resize(outputFrames);
|
|
84
|
+
|
|
85
|
+
buffer = stretchedBuffer;
|
|
86
|
+
|
|
87
|
+
stretch_deinit(stretcher);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static AudioFormat detectAudioFormat(const void* data, size_t size) {
|
|
91
|
+
if (size < 12) return AudioFormat::UNKNOWN;
|
|
92
|
+
const auto* bytes = static_cast<const unsigned char*>(data);
|
|
36
93
|
|
|
37
94
|
// WAV/RIFF
|
|
38
95
|
if (std::memcmp(bytes, "RIFF", 4) == 0 && std::memcmp(bytes + 8, "WAVE", 4) == 0)
|
|
39
|
-
|
|
96
|
+
return AudioFormat::WAV;
|
|
40
97
|
|
|
41
98
|
// OGG
|
|
42
99
|
if (std::memcmp(bytes, "OggS", 4) == 0)
|
|
43
|
-
|
|
100
|
+
return AudioFormat::OGG;
|
|
44
101
|
|
|
45
102
|
// FLAC
|
|
46
103
|
if (std::memcmp(bytes, "fLaC", 4) == 0)
|
|
47
|
-
|
|
104
|
+
return AudioFormat::FLAC;
|
|
48
105
|
|
|
49
106
|
// AAC starts with 0xFF 0xF1 or 0xFF 0xF9
|
|
50
107
|
if (bytes[0] == 0xFF && (bytes[1] & 0xF6) == 0xF0)
|
|
51
|
-
|
|
108
|
+
return AudioFormat::AAC;
|
|
52
109
|
|
|
53
110
|
// MP3: "ID3" or 11-bit frame sync (0xFF 0xE0)
|
|
54
111
|
if (std::memcmp(bytes, "ID3", 3) == 0)
|
|
55
|
-
|
|
112
|
+
return AudioFormat::MP3;
|
|
56
113
|
if (bytes[0] == 0xFF && (bytes[1] & 0xE0) == 0xE0)
|
|
57
|
-
|
|
114
|
+
return AudioFormat::MP3;
|
|
58
115
|
|
|
59
116
|
if (std::memcmp(bytes + 4, "ftyp", 4) == 0) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
117
|
+
if (std::memcmp(bytes + 8, "M4A ", 4) == 0)
|
|
118
|
+
return AudioFormat::M4A;
|
|
119
|
+
else if (std::memcmp(bytes + 8, "qt ", 4) == 0)
|
|
120
|
+
return AudioFormat::MOV;
|
|
121
|
+
return AudioFormat::MP4;
|
|
65
122
|
}
|
|
66
123
|
return AudioFormat::UNKNOWN;
|
|
67
124
|
}
|
|
@@ -69,21 +126,19 @@ class AudioDecoder {
|
|
|
69
126
|
static inline bool pathHasExtension(const std::string &path, const std::vector<std::string> &extensions) {
|
|
70
127
|
std::string pathLower = path;
|
|
71
128
|
std::transform(pathLower.begin(), pathLower.end(), pathLower.begin(), ::tolower);
|
|
72
|
-
for (const auto
|
|
73
|
-
|
|
74
|
-
|
|
129
|
+
for (const auto& ext : extensions) {
|
|
130
|
+
if (pathLower.ends_with(ext))
|
|
131
|
+
return true;
|
|
75
132
|
}
|
|
76
133
|
return false;
|
|
77
134
|
}
|
|
78
135
|
|
|
136
|
+
|
|
79
137
|
[[nodiscard]] static inline int16_t floatToInt16(float sample) {
|
|
80
|
-
return static_cast<int16_t>(sample *
|
|
138
|
+
return static_cast<int16_t>(sample * 32768.0f);
|
|
81
139
|
}
|
|
82
140
|
[[nodiscard]] static inline float int16ToFloat(int16_t sample) {
|
|
83
|
-
return static_cast<float>(sample) /
|
|
84
|
-
}
|
|
85
|
-
[[nodiscard]] static inline float uint8ToFloat(uint8_t byte1, uint8_t byte2) {
|
|
86
|
-
return static_cast<float>(static_cast<int16_t>((byte2 << 8) | byte1)) / INT16_MAX;
|
|
141
|
+
return static_cast<float>(sample) / 32768.0f;
|
|
87
142
|
}
|
|
88
143
|
};
|
|
89
144
|
|
|
@@ -10,10 +10,6 @@ namespace audioapi {
|
|
|
10
10
|
static constexpr int RENDER_QUANTUM_SIZE = 128;
|
|
11
11
|
static constexpr size_t MAX_FFT_SIZE = 32768;
|
|
12
12
|
|
|
13
|
-
// stretcher
|
|
14
|
-
static constexpr float UPPER_FREQUENCY_LIMIT_DETECTION = 333.0f;
|
|
15
|
-
static constexpr float LOWER_FREQUENCY_LIMIT_DETECTION = 55.0f;
|
|
16
|
-
|
|
17
13
|
// general
|
|
18
14
|
static constexpr float MOST_POSITIVE_SINGLE_FLOAT = static_cast<float>(std::numeric_limits<float>::max());
|
|
19
15
|
static constexpr float MOST_NEGATIVE_SINGLE_FLOAT = static_cast<float>(std::numeric_limits<float>::lowest());
|
|
@@ -156,8 +156,12 @@ void AudioEventHandlerRegistry::invokeHandlerWithEventBody(
|
|
|
156
156
|
|
|
157
157
|
// In case of debugging this, please increment the hours spent counter
|
|
158
158
|
|
|
159
|
-
// Hours spent on this:
|
|
159
|
+
// Hours spent on this: 8
|
|
160
160
|
try {
|
|
161
|
+
if (!handlerIt->second || !handlerIt->second->isFunction(*runtime_)) {
|
|
162
|
+
// If the handler is not valid, we can skip it
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
161
165
|
jsi::Object eventObject(*runtime_);
|
|
162
166
|
// handle special logic for microphone, because we pass audio buffer which
|
|
163
167
|
// has significant size
|
|
Binary file
|