react-native-audio-api 0.9.0 → 0.9.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/RNAudioAPI.podspec +5 -2
- package/android/build.gradle +26 -2
- package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +5 -0
- package/android/src/main/cpp/audioapi/android/AudioAPIModule.h +1 -0
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +4 -2
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +29 -1
- 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 +37 -6
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +12 -5
- package/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp +3 -8
- package/common/cpp/audioapi/core/BaseAudioContext.cpp +12 -6
- package/common/cpp/audioapi/core/BaseAudioContext.h +14 -3
- package/common/cpp/audioapi/core/effects/WorkletNode.cpp +39 -52
- package/common/cpp/audioapi/core/effects/WorkletNode.h +6 -10
- package/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +17 -19
- package/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +2 -5
- 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 +14 -16
- package/common/cpp/audioapi/core/sources/WorkletSourceNode.h +2 -5
- package/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h +32 -13
- package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.cpp +75 -2
- package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.h +50 -36
- 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/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 +7 -4
- 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/AudioAPIModule.h +2 -1
- package/ios/audioapi/ios/AudioAPIModule.mm +2 -0
- package/ios/audioapi/ios/core/IOSAudioRecorder.h +2 -1
- package/lib/commonjs/core/AudioContext.js +1 -5
- package/lib/commonjs/core/AudioContext.js.map +1 -1
- package/lib/commonjs/core/BaseAudioContext.js +16 -25
- package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
- package/lib/commonjs/core/OfflineAudioContext.js +1 -5
- package/lib/commonjs/core/OfflineAudioContext.js.map +1 -1
- package/lib/commonjs/utils/index.js +20 -4
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/module/core/AudioContext.js +2 -6
- package/lib/module/core/AudioContext.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +17 -26
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/module/core/OfflineAudioContext.js +2 -6
- package/lib/module/core/OfflineAudioContext.js.map +1 -1
- package/lib/module/utils/index.js +16 -1
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/core/AudioContext.d.ts +0 -1
- package/lib/typescript/core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/core/OfflineAudioContext.d.ts +0 -1
- package/lib/typescript/core/OfflineAudioContext.d.ts.map +1 -1
- package/lib/typescript/utils/index.d.ts +4 -1
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/package.json +4 -1
- package/scripts/rnaa_utils.rb +8 -0
- package/scripts/validate-worklets-version.js +28 -0
- package/src/core/AudioContext.ts +3 -7
- package/src/core/BaseAudioContext.ts +44 -60
- package/src/core/OfflineAudioContext.ts +2 -7
- package/src/utils/index.ts +23 -1
|
@@ -5,9 +5,8 @@ namespace audioapi {
|
|
|
5
5
|
|
|
6
6
|
WorkletProcessingNode::WorkletProcessingNode(
|
|
7
7
|
BaseAudioContext *context,
|
|
8
|
-
|
|
9
|
-
std::
|
|
10
|
-
: AudioNode(context), workletRunner_(runtime), shareableWorklet_(worklet) {
|
|
8
|
+
WorkletsRunner &&workletRunner)
|
|
9
|
+
: AudioNode(context), workletRunner_(std::move(workletRunner)) {
|
|
11
10
|
isInitialized_ = true;
|
|
12
11
|
|
|
13
12
|
// Pre-allocate buffers for max 128 frames and 2 channels (stereo)
|
|
@@ -16,13 +15,12 @@ WorkletProcessingNode::WorkletProcessingNode(
|
|
|
16
15
|
outputBuffsHandles_.resize(maxChannelCount);
|
|
17
16
|
|
|
18
17
|
for (size_t i = 0; i < maxChannelCount; ++i) {
|
|
19
|
-
auto
|
|
20
|
-
inputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(
|
|
21
|
-
inputBuff, RENDER_QUANTUM_SIZE * sizeof(float));
|
|
18
|
+
auto inputAudioArray = std::make_shared<AudioArray>(RENDER_QUANTUM_SIZE);
|
|
19
|
+
inputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(inputAudioArray);
|
|
22
20
|
|
|
23
|
-
auto
|
|
24
|
-
outputBuffsHandles_[i] =
|
|
25
|
-
|
|
21
|
+
auto outputAudioArray = std::make_shared<AudioArray>(RENDER_QUANTUM_SIZE);
|
|
22
|
+
outputBuffsHandles_[i] =
|
|
23
|
+
std::make_shared<AudioArrayBuffer>(outputAudioArray);
|
|
26
24
|
}
|
|
27
25
|
}
|
|
28
26
|
|
|
@@ -43,8 +41,8 @@ std::shared_ptr<AudioBus> WorkletProcessingNode::processNode(
|
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
// Execute the worklet
|
|
46
|
-
auto result = workletRunner_.
|
|
47
|
-
[this, channelCount, framesToProcess](jsi::Runtime &rt) {
|
|
44
|
+
auto result = workletRunner_.executeOnRuntimeSync(
|
|
45
|
+
[this, channelCount, framesToProcess](jsi::Runtime &rt) -> jsi::Value {
|
|
48
46
|
auto inputJsArray = jsi::Array(rt, channelCount);
|
|
49
47
|
auto outputJsArray = jsi::Array(rt, channelCount);
|
|
50
48
|
|
|
@@ -59,14 +57,14 @@ std::shared_ptr<AudioBus> WorkletProcessingNode::processNode(
|
|
|
59
57
|
outputJsArray.setValueAtIndex(rt, ch, outputArrayBuffer);
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
// We call unsafely here because we are already on the runtime thread
|
|
61
|
+
// and the runtime is locked by executeOnRuntimeSync (if
|
|
62
|
+
// shouldLockRuntime is true)
|
|
63
|
+
return workletRunner_.callUnsafe(
|
|
64
|
+
inputJsArray,
|
|
65
|
+
outputJsArray,
|
|
66
|
+
jsi::Value(rt, static_cast<int>(framesToProcess)),
|
|
67
|
+
jsi::Value(rt, this->context_->getCurrentTime()));
|
|
70
68
|
});
|
|
71
69
|
|
|
72
70
|
// Copy processed output data back to the processing bus or zero on failure
|
|
@@ -18,8 +18,7 @@ class WorkletProcessingNode : public AudioNode {
|
|
|
18
18
|
public:
|
|
19
19
|
explicit WorkletProcessingNode(
|
|
20
20
|
BaseAudioContext *context,
|
|
21
|
-
|
|
22
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime
|
|
21
|
+
WorkletsRunner &&workletRunner
|
|
23
22
|
) : AudioNode(context) {}
|
|
24
23
|
|
|
25
24
|
protected:
|
|
@@ -33,8 +32,7 @@ class WorkletProcessingNode : public AudioNode {
|
|
|
33
32
|
public:
|
|
34
33
|
explicit WorkletProcessingNode(
|
|
35
34
|
BaseAudioContext *context,
|
|
36
|
-
|
|
37
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime
|
|
35
|
+
WorkletsRunner &&workletRunner
|
|
38
36
|
);
|
|
39
37
|
|
|
40
38
|
protected:
|
|
@@ -42,7 +40,6 @@ class WorkletProcessingNode : public AudioNode {
|
|
|
42
40
|
|
|
43
41
|
private:
|
|
44
42
|
WorkletsRunner workletRunner_;
|
|
45
|
-
std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
|
|
46
43
|
std::vector<std::shared_ptr<AudioArrayBuffer>> inputBuffsHandles_;
|
|
47
44
|
std::vector<std::shared_ptr<AudioArrayBuffer>> outputBuffsHandles_;
|
|
48
45
|
};
|
|
@@ -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
|
*/
|
|
@@ -5,20 +5,17 @@ namespace audioapi {
|
|
|
5
5
|
|
|
6
6
|
WorkletSourceNode::WorkletSourceNode(
|
|
7
7
|
BaseAudioContext *context,
|
|
8
|
-
|
|
9
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime)
|
|
8
|
+
WorkletsRunner &&workletRunner)
|
|
10
9
|
: AudioScheduledSourceNode(context),
|
|
11
|
-
workletRunner_(
|
|
12
|
-
shareableWorklet_(worklet) {
|
|
10
|
+
workletRunner_(std::move(workletRunner)) {
|
|
13
11
|
isInitialized_ = true;
|
|
14
12
|
|
|
15
13
|
// Prepare buffers for audio processing
|
|
16
14
|
size_t outputChannelCount = this->getChannelCount();
|
|
17
15
|
outputBuffsHandles_.resize(outputChannelCount);
|
|
18
16
|
for (size_t i = 0; i < outputChannelCount; ++i) {
|
|
19
|
-
auto
|
|
20
|
-
outputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(
|
|
21
|
-
buff, RENDER_QUANTUM_SIZE * sizeof(float));
|
|
17
|
+
auto audioArray = std::make_shared<AudioArray>(RENDER_QUANTUM_SIZE);
|
|
18
|
+
outputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(audioArray);
|
|
22
19
|
}
|
|
23
20
|
}
|
|
24
21
|
|
|
@@ -43,21 +40,22 @@ std::shared_ptr<AudioBus> WorkletSourceNode::processNode(
|
|
|
43
40
|
|
|
44
41
|
size_t outputChannelCount = processingBus->getNumberOfChannels();
|
|
45
42
|
|
|
46
|
-
auto result = workletRunner_.
|
|
43
|
+
auto result = workletRunner_.executeOnRuntimeSync(
|
|
47
44
|
[this, nonSilentFramesToProcess, startOffset](jsi::Runtime &rt) {
|
|
48
45
|
auto jsiArray = jsi::Array(rt, this->outputBuffsHandles_.size());
|
|
49
46
|
for (size_t i = 0; i < this->outputBuffsHandles_.size(); ++i) {
|
|
50
47
|
auto arrayBuffer = jsi::ArrayBuffer(rt, this->outputBuffsHandles_[i]);
|
|
51
48
|
jsiArray.setValueAtIndex(rt, i, arrayBuffer);
|
|
52
49
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
|
|
51
|
+
// We call unsafely here because we are already on the runtime thread
|
|
52
|
+
// and the runtime is locked by executeOnRuntimeSync (if
|
|
53
|
+
// shouldLockRuntime is true)
|
|
54
|
+
return workletRunner_.callUnsafe(
|
|
55
|
+
jsiArray,
|
|
56
|
+
jsi::Value(rt, static_cast<int>(nonSilentFramesToProcess)),
|
|
57
|
+
jsi::Value(rt, this->context_->getCurrentTime()),
|
|
58
|
+
jsi::Value(rt, static_cast<int>(startOffset)));
|
|
61
59
|
});
|
|
62
60
|
|
|
63
61
|
// If the worklet execution failed, zero the output
|
|
@@ -18,8 +18,7 @@ class WorkletSourceNode : public AudioScheduledSourceNode {
|
|
|
18
18
|
public:
|
|
19
19
|
explicit WorkletSourceNode(
|
|
20
20
|
BaseAudioContext *context,
|
|
21
|
-
|
|
22
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime
|
|
21
|
+
WorkletsRunner &&workletRunner
|
|
23
22
|
) : AudioScheduledSourceNode(context) {}
|
|
24
23
|
|
|
25
24
|
protected:
|
|
@@ -31,15 +30,13 @@ class WorkletSourceNode : public AudioScheduledSourceNode {
|
|
|
31
30
|
public:
|
|
32
31
|
explicit WorkletSourceNode(
|
|
33
32
|
BaseAudioContext *context,
|
|
34
|
-
|
|
35
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime
|
|
33
|
+
WorkletsRunner &&workletRunner
|
|
36
34
|
);
|
|
37
35
|
|
|
38
36
|
protected:
|
|
39
37
|
std::shared_ptr<AudioBus> processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
|
|
40
38
|
private:
|
|
41
39
|
WorkletsRunner workletRunner_;
|
|
42
|
-
std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
|
|
43
40
|
std::vector<std::shared_ptr<AudioArrayBuffer>> outputBuffsHandles_;
|
|
44
41
|
};
|
|
45
42
|
#endif // RN_AUDIO_API_TEST
|
|
@@ -5,16 +5,6 @@
|
|
|
5
5
|
#include <string>
|
|
6
6
|
#include <memory>
|
|
7
7
|
|
|
8
|
-
#ifdef __APPLE__
|
|
9
|
-
/// We cannot make any conditional logic inside podspec but it should automatically compile those files
|
|
10
|
-
/// they should be accessible if someone has react-native-worklets in node_modules
|
|
11
|
-
#if __has_include(<worklets/WorkletRuntime/WorkletRuntime.h>)
|
|
12
|
-
#define RN_AUDIO_API_ENABLE_WORKLETS 1
|
|
13
|
-
#else
|
|
14
|
-
#define RN_AUDIO_API_ENABLE_WORKLETS 0
|
|
15
|
-
#endif
|
|
16
|
-
#endif
|
|
17
|
-
|
|
18
8
|
#ifndef RN_AUDIO_API_TEST
|
|
19
9
|
#define RN_AUDIO_API_TEST 0
|
|
20
10
|
#endif
|
|
@@ -27,6 +17,11 @@
|
|
|
27
17
|
#include <worklets/android/WorkletsModule.h>
|
|
28
18
|
#endif
|
|
29
19
|
#else
|
|
20
|
+
|
|
21
|
+
#define RN_AUDIO_API_WORKLETS_DISABLED_ERROR \
|
|
22
|
+
std::runtime_error( \
|
|
23
|
+
"Worklets are disabled. Please install react-native-worklets or check if you have supported version to enable these features.");
|
|
24
|
+
|
|
30
25
|
/// @brief Dummy implementation of worklets for non-worklet builds they should do nothing and mock necessary methods
|
|
31
26
|
/// @note It helps to reduce compile time branching across codebase
|
|
32
27
|
/// @note If you need to base some c++ implementation on if the worklets are enabled use `#if RN_AUDIO_API_ENABLE_WORKLETS`
|
|
@@ -36,17 +31,41 @@ using namespace facebook;
|
|
|
36
31
|
class MessageQueueThread {};
|
|
37
32
|
class WorkletsModuleProxy {};
|
|
38
33
|
class WorkletRuntime {
|
|
39
|
-
|
|
34
|
+
public:
|
|
35
|
+
explicit WorkletRuntime(uint64_t, const std::shared_ptr<MessageQueueThread> &, const std::string &, const bool) {
|
|
36
|
+
throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
|
|
37
|
+
}
|
|
38
|
+
jsi::Runtime &getJSIRuntime() const {
|
|
39
|
+
throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
|
|
40
|
+
}
|
|
41
|
+
jsi::Value executeSync(jsi::Runtime &rt, const jsi::Value &worklet) const {
|
|
42
|
+
throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
|
|
43
|
+
}
|
|
44
|
+
jsi::Value executeSync(std::function<jsi::Value(jsi::Runtime &)> &&job) const {
|
|
45
|
+
throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
|
|
46
|
+
}
|
|
47
|
+
jsi::Value executeSync(const std::function<jsi::Value(jsi::Runtime &)> &job) const {
|
|
48
|
+
throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
|
|
49
|
+
}
|
|
40
50
|
};
|
|
41
51
|
class SerializableWorklet {
|
|
42
|
-
|
|
52
|
+
public:
|
|
53
|
+
SerializableWorklet(jsi::Runtime*, const jsi::Object &) {
|
|
54
|
+
throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
|
|
55
|
+
}
|
|
56
|
+
jsi::Value toJSValue(jsi::Runtime &rt) {
|
|
57
|
+
throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
|
|
58
|
+
}
|
|
43
59
|
};
|
|
44
60
|
} // namespace worklets
|
|
61
|
+
|
|
62
|
+
#undef RN_AUDIO_API_WORKLETS_DISABLED_ERROR
|
|
63
|
+
|
|
45
64
|
#endif
|
|
46
65
|
|
|
47
66
|
/// @brief Struct to hold references to different runtimes used in the AudioAPI
|
|
48
67
|
/// @note it is used to pass them around and avoid creating multiple instances of the same runtime
|
|
49
68
|
struct RuntimeRegistry {
|
|
50
69
|
std::weak_ptr<worklets::WorkletRuntime> uiRuntime;
|
|
51
|
-
std::
|
|
70
|
+
std::shared_ptr<worklets::WorkletRuntime> audioRuntime;
|
|
52
71
|
};
|
|
@@ -3,7 +3,80 @@
|
|
|
3
3
|
namespace audioapi {
|
|
4
4
|
|
|
5
5
|
WorkletsRunner::WorkletsRunner(
|
|
6
|
-
std::weak_ptr<worklets::WorkletRuntime>
|
|
7
|
-
|
|
6
|
+
std::weak_ptr<worklets::WorkletRuntime> weakRuntime,
|
|
7
|
+
std::shared_ptr<worklets::SerializableWorklet> shareableWorklet,
|
|
8
|
+
bool shouldLockRuntime)
|
|
9
|
+
: weakRuntime_(std::move(weakRuntime)),
|
|
10
|
+
shouldLockRuntime(shouldLockRuntime) {
|
|
11
|
+
auto strongRuntime = weakRuntime_.lock();
|
|
12
|
+
if (strongRuntime == nullptr) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
#if RN_AUDIO_API_ENABLE_WORKLETS
|
|
16
|
+
unsafeRuntimePtr = &strongRuntime->getJSIRuntime();
|
|
17
|
+
strongRuntime->executeSync(
|
|
18
|
+
[this, shareableWorklet](jsi::Runtime &rt) -> jsi::Value {
|
|
19
|
+
/// Placement new to avoid dynamic memory allocation
|
|
20
|
+
new (reinterpret_cast<jsi::Function *>(&unsafeWorklet))
|
|
21
|
+
jsi::Function(shareableWorklet->toJSValue(*unsafeRuntimePtr)
|
|
22
|
+
.asObject(*unsafeRuntimePtr)
|
|
23
|
+
.asFunction(*unsafeRuntimePtr));
|
|
24
|
+
return jsi::Value::undefined();
|
|
25
|
+
});
|
|
26
|
+
workletInitialized = true;
|
|
27
|
+
#else
|
|
28
|
+
unsafeRuntimePtr = nullptr;
|
|
29
|
+
workletInitialized = false;
|
|
30
|
+
#endif
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
WorkletsRunner::WorkletsRunner(WorkletsRunner &&other)
|
|
34
|
+
: weakRuntime_(std::move(other.weakRuntime_)),
|
|
35
|
+
unsafeRuntimePtr(other.unsafeRuntimePtr),
|
|
36
|
+
shouldLockRuntime(other.shouldLockRuntime),
|
|
37
|
+
workletInitialized(other.workletInitialized) {
|
|
38
|
+
if (workletInitialized) {
|
|
39
|
+
std::memcpy(&unsafeWorklet, &other.unsafeWorklet, sizeof(unsafeWorklet));
|
|
40
|
+
other.workletInitialized = false;
|
|
41
|
+
other.unsafeRuntimePtr = nullptr;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
WorkletsRunner::~WorkletsRunner() {
|
|
46
|
+
if (!workletInitialized) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
auto strongRuntime = weakRuntime_.lock();
|
|
50
|
+
if (strongRuntime == nullptr) {
|
|
51
|
+
// We cannot safely destroy the worklet without a valid runtime
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
reinterpret_cast<jsi::Function *>(&unsafeWorklet)->~Function();
|
|
55
|
+
workletInitialized = false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
std::optional<jsi::Value> WorkletsRunner::executeOnRuntimeGuarded(
|
|
59
|
+
const std::function<jsi::Value(jsi::Runtime &)> &&job) const
|
|
60
|
+
noexcept(noexcept(job)) {
|
|
61
|
+
auto strongRuntime = weakRuntime_.lock();
|
|
62
|
+
if (strongRuntime == nullptr) {
|
|
63
|
+
return std::nullopt;
|
|
64
|
+
}
|
|
65
|
+
#if RN_AUDIO_API_ENABLE_WORKLETS
|
|
66
|
+
return strongRuntime->executeSync(std::move(job));
|
|
67
|
+
#else
|
|
68
|
+
return std::nullopt;
|
|
69
|
+
#endif
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
std::optional<jsi::Value> WorkletsRunner::executeOnRuntimeUnsafe(
|
|
73
|
+
const std::function<jsi::Value(jsi::Runtime &)> &&job) const
|
|
74
|
+
noexcept(noexcept(job)) {
|
|
75
|
+
#if RN_AUDIO_API_ENABLE_WORKLETS
|
|
76
|
+
return job(*unsafeRuntimePtr);
|
|
77
|
+
#else
|
|
78
|
+
return std::nullopt;
|
|
79
|
+
#endif
|
|
80
|
+
};
|
|
8
81
|
|
|
9
82
|
}; // namespace audioapi
|
|
@@ -26,48 +26,62 @@ using namespace facebook;
|
|
|
26
26
|
|
|
27
27
|
class WorkletsRunner {
|
|
28
28
|
public:
|
|
29
|
-
|
|
29
|
+
explicit WorkletsRunner(
|
|
30
|
+
std::weak_ptr<worklets::WorkletRuntime> weakRuntime,
|
|
31
|
+
std::shared_ptr<worklets::SerializableWorklet> shareableWorklet,
|
|
32
|
+
bool shouldLockRuntime = true);
|
|
33
|
+
WorkletsRunner(WorkletsRunner&&);
|
|
34
|
+
~WorkletsRunner();
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
#if RN_AUDIO_API_ENABLE_WORKLETS
|
|
41
|
-
return strongRuntime->executeSync(std::move(job));
|
|
42
|
-
#else
|
|
43
|
-
return std::nullopt;
|
|
44
|
-
#endif
|
|
45
|
-
}
|
|
36
|
+
/// @brief Call the worklet function with the given arguments.
|
|
37
|
+
/// @tparam ...Args
|
|
38
|
+
/// @param ...args
|
|
39
|
+
/// @return The result of the worklet function call.
|
|
40
|
+
/// @note This method is unsafe and should be used with caution. It assumes that the runtime and worklet are valid and runtime is locked.
|
|
41
|
+
template<typename... Args>
|
|
42
|
+
inline jsi::Value callUnsafe(Args&&... args) {
|
|
43
|
+
return getUnsafeWorklet().call(*unsafeRuntimePtr, std::forward<Args>(args)...);
|
|
44
|
+
}
|
|
46
45
|
|
|
47
|
-
/// @brief Execute a worklet with the given arguments.
|
|
48
|
-
/// @tparam ...Args
|
|
49
|
-
/// @param shareableWorklet
|
|
50
|
-
/// @param ...args
|
|
51
|
-
/// @note Execution is synchronous, this method can be used in `executeOnRuntimeGuardedSync` and `...Async` methods arguments
|
|
52
|
-
/// @return nullopt if the runtime is not available or the result of the worklet execution
|
|
53
|
-
template<typename... Args>
|
|
54
|
-
std::optional<jsi::Value> executeWorklet(const std::shared_ptr<worklets::SerializableWorklet>& shareableWorklet, Args&&... args) {
|
|
55
|
-
auto strongRuntime = weakUiRuntime_.lock();
|
|
56
|
-
if (strongRuntime == nullptr) {
|
|
57
|
-
return std::nullopt;
|
|
58
|
-
}
|
|
59
46
|
|
|
60
|
-
|
|
47
|
+
/// @brief Call the worklet function with the given arguments.
|
|
48
|
+
/// @tparam ...Args
|
|
49
|
+
/// @param ...args
|
|
50
|
+
/// @return The result of the worklet function call.
|
|
51
|
+
/// @note This method is safe and will check if the runtime is available before calling the worklet. If the runtime is not available, it will return nullopt.
|
|
52
|
+
template<typename... Args>
|
|
53
|
+
inline std::optional<jsi::Value> call(Args&&... args) {
|
|
54
|
+
return executeOnRuntimeGuarded([this, args...](jsi::Runtime &rt) -> jsi::Value {
|
|
55
|
+
return callUnsafe(std::forward<Args>(args)...);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
/// @brief Execute a job on the UI runtime safely.
|
|
60
|
+
/// @param job
|
|
61
|
+
/// @return nullopt if the runtime is not available or the result of the job execution
|
|
62
|
+
/// @note Execution is synchronous and will be guarded if shouldLockRuntime is true.
|
|
63
|
+
inline std::optional<jsi::Value> executeOnRuntimeSync(const std::function<jsi::Value(jsi::Runtime&)>&& job) const noexcept(noexcept(job)) {
|
|
64
|
+
if (shouldLockRuntime) return executeOnRuntimeGuarded(std::move(job));
|
|
65
|
+
else return executeOnRuntimeUnsafe(std::move(job));
|
|
66
|
+
}
|
|
68
67
|
|
|
69
68
|
private:
|
|
70
|
-
|
|
69
|
+
std::weak_ptr<worklets::WorkletRuntime> weakRuntime_;
|
|
70
|
+
jsi::Runtime* unsafeRuntimePtr = nullptr;
|
|
71
|
+
|
|
72
|
+
/// @note We want to avoid automatic destruction as
|
|
73
|
+
/// when runtime is destroyed, underlying pointer will be invalid
|
|
74
|
+
char unsafeWorklet[sizeof(jsi::Function)];
|
|
75
|
+
bool workletInitialized = false;
|
|
76
|
+
bool shouldLockRuntime = true;
|
|
77
|
+
|
|
78
|
+
inline jsi::Function &getUnsafeWorklet() {
|
|
79
|
+
return *reinterpret_cast<jsi::Function*>(&unsafeWorklet);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
std::optional<jsi::Value> executeOnRuntimeGuarded(const std::function<jsi::Value(jsi::Runtime&)>&& job) const noexcept(noexcept(job));
|
|
83
|
+
|
|
84
|
+
std::optional<jsi::Value> executeOnRuntimeUnsafe(const std::function<jsi::Value(jsi::Runtime&)>&& job) const noexcept(noexcept(job));
|
|
71
85
|
};
|
|
72
86
|
|
|
73
87
|
} // namespace audioapi
|
|
@@ -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
|
|
Binary file
|
|
@@ -8,32 +8,32 @@
|
|
|
8
8
|
<key>BinaryPath</key>
|
|
9
9
|
<string>libavformat.framework/libavformat</string>
|
|
10
10
|
<key>LibraryIdentifier</key>
|
|
11
|
-
<string>ios-
|
|
11
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
12
12
|
<key>LibraryPath</key>
|
|
13
13
|
<string>libavformat.framework</string>
|
|
14
14
|
<key>SupportedArchitectures</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>arm64</string>
|
|
17
|
+
<string>x86_64</string>
|
|
17
18
|
</array>
|
|
18
19
|
<key>SupportedPlatform</key>
|
|
19
20
|
<string>ios</string>
|
|
21
|
+
<key>SupportedPlatformVariant</key>
|
|
22
|
+
<string>simulator</string>
|
|
20
23
|
</dict>
|
|
21
24
|
<dict>
|
|
22
25
|
<key>BinaryPath</key>
|
|
23
26
|
<string>libavformat.framework/libavformat</string>
|
|
24
27
|
<key>LibraryIdentifier</key>
|
|
25
|
-
<string>ios-
|
|
28
|
+
<string>ios-arm64</string>
|
|
26
29
|
<key>LibraryPath</key>
|
|
27
30
|
<string>libavformat.framework</string>
|
|
28
31
|
<key>SupportedArchitectures</key>
|
|
29
32
|
<array>
|
|
30
33
|
<string>arm64</string>
|
|
31
|
-
<string>x86_64</string>
|
|
32
34
|
</array>
|
|
33
35
|
<key>SupportedPlatform</key>
|
|
34
36
|
<string>ios</string>
|
|
35
|
-
<key>SupportedPlatformVariant</key>
|
|
36
|
-
<string>simulator</string>
|
|
37
37
|
</dict>
|
|
38
38
|
</array>
|
|
39
39
|
<key>CFBundlePackageType</key>
|
|
Binary file
|
|
Binary file
|