react-native-audio-api 0.10.0-nightly-034f768-20251020 → 0.10.0-nightly-2d11b56-20251021

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.
Files changed (56) hide show
  1. package/android/build.gradle +1 -0
  2. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +12 -5
  3. package/common/cpp/audioapi/core/BaseAudioContext.cpp +12 -6
  4. package/common/cpp/audioapi/core/BaseAudioContext.h +14 -3
  5. package/common/cpp/audioapi/core/effects/WorkletNode.cpp +31 -32
  6. package/common/cpp/audioapi/core/effects/WorkletNode.h +4 -7
  7. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +12 -13
  8. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +2 -5
  9. package/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +12 -13
  10. package/common/cpp/audioapi/core/sources/WorkletSourceNode.h +2 -5
  11. package/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h +32 -3
  12. package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.cpp +75 -2
  13. package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.h +50 -36
  14. package/ios/audioapi/ios/core/IOSAudioRecorder.h +2 -1
  15. package/lib/commonjs/api.js +43 -0
  16. package/lib/commonjs/api.js.map +1 -1
  17. package/lib/commonjs/api.web.js +50 -0
  18. package/lib/commonjs/api.web.js.map +1 -1
  19. package/lib/commonjs/core/AudioContext.js +0 -4
  20. package/lib/commonjs/core/AudioContext.js.map +1 -1
  21. package/lib/commonjs/core/OfflineAudioContext.js +0 -4
  22. package/lib/commonjs/core/OfflineAudioContext.js.map +1 -1
  23. package/lib/commonjs/utils/index.js +1 -0
  24. package/lib/commonjs/utils/index.js.map +1 -1
  25. package/lib/commonjs/web-core/AudioScheduledSourceNode.js.map +1 -1
  26. package/lib/module/api.js +1 -0
  27. package/lib/module/api.js.map +1 -1
  28. package/lib/module/api.web.js +1 -0
  29. package/lib/module/api.web.js.map +1 -1
  30. package/lib/module/core/AudioContext.js +0 -4
  31. package/lib/module/core/AudioContext.js.map +1 -1
  32. package/lib/module/core/OfflineAudioContext.js +0 -4
  33. package/lib/module/core/OfflineAudioContext.js.map +1 -1
  34. package/lib/module/utils/index.js +1 -0
  35. package/lib/module/utils/index.js.map +1 -1
  36. package/lib/module/web-core/AudioScheduledSourceNode.js.map +1 -1
  37. package/lib/typescript/api.d.ts +1 -0
  38. package/lib/typescript/api.d.ts.map +1 -1
  39. package/lib/typescript/api.web.d.ts +1 -0
  40. package/lib/typescript/api.web.d.ts.map +1 -1
  41. package/lib/typescript/core/AudioContext.d.ts +0 -1
  42. package/lib/typescript/core/AudioContext.d.ts.map +1 -1
  43. package/lib/typescript/core/OfflineAudioContext.d.ts +0 -1
  44. package/lib/typescript/core/OfflineAudioContext.d.ts.map +1 -1
  45. package/lib/typescript/web-core/AudioBufferSourceNode.d.ts +2 -2
  46. package/lib/typescript/web-core/AudioBufferSourceNode.d.ts.map +1 -1
  47. package/lib/typescript/web-core/AudioScheduledSourceNode.d.ts +1 -1
  48. package/lib/typescript/web-core/AudioScheduledSourceNode.d.ts.map +1 -1
  49. package/package.json +8 -8
  50. package/src/api.ts +10 -0
  51. package/src/api.web.ts +10 -0
  52. package/src/core/AudioContext.ts +0 -5
  53. package/src/core/OfflineAudioContext.ts +0 -5
  54. package/src/utils/index.ts +1 -0
  55. package/src/web-core/AudioBufferSourceNode.tsx +2 -2
  56. package/src/web-core/AudioScheduledSourceNode.tsx +1 -1
@@ -165,6 +165,7 @@ android {
165
165
  "**/libfolly_runtime.so",
166
166
  "**/libglog.so",
167
167
  "**/libhermes.so",
168
+ "**/libhermesvm.so",
168
169
  "**/libhermes-executor-debug.so",
169
170
  "**/libhermes_executor.so",
170
171
  "**/libhermestooling.so",
@@ -77,14 +77,15 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWorkletSourceNode) {
77
77
  runtime, args[0]);
78
78
  std::weak_ptr<worklets::WorkletRuntime> workletRuntime;
79
79
  auto shouldUseUiRuntime = args[1].getBool();
80
+ auto shouldLockRuntime = shouldUseUiRuntime;
80
81
  if (shouldUseUiRuntime) {
81
82
  workletRuntime = context_->runtimeRegistry_.uiRuntime;
82
83
  } else {
83
84
  workletRuntime = context_->runtimeRegistry_.audioRuntime;
84
85
  }
85
86
 
86
- auto workletSourceNode =
87
- context_->createWorkletSourceNode(shareableWorklet, workletRuntime);
87
+ auto workletSourceNode = context_->createWorkletSourceNode(
88
+ shareableWorklet, workletRuntime, shouldLockRuntime);
88
89
  auto workletSourceNodeHostObject =
89
90
  std::make_shared<WorkletSourceNodeHostObject>(workletSourceNode);
90
91
  return jsi::Object::createFromHostObject(
@@ -101,6 +102,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWorkletNode) {
101
102
 
102
103
  std::weak_ptr<worklets::WorkletRuntime> workletRuntime;
103
104
  auto shouldUseUiRuntime = args[1].getBool();
105
+ auto shouldLockRuntime = shouldUseUiRuntime;
104
106
  if (shouldUseUiRuntime) {
105
107
  workletRuntime = context_->runtimeRegistry_.uiRuntime;
106
108
  } else {
@@ -110,7 +112,11 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWorkletNode) {
110
112
  auto inputChannelCount = static_cast<size_t>(args[3].getNumber());
111
113
 
112
114
  auto workletNode = context_->createWorkletNode(
113
- shareableWorklet, workletRuntime, bufferLength, inputChannelCount);
115
+ shareableWorklet,
116
+ workletRuntime,
117
+ bufferLength,
118
+ inputChannelCount,
119
+ shouldLockRuntime);
114
120
  auto workletNodeHostObject =
115
121
  std::make_shared<WorkletNodeHostObject>(workletNode);
116
122
  return jsi::Object::createFromHostObject(runtime, workletNodeHostObject);
@@ -128,14 +134,15 @@ JSI_HOST_FUNCTION_IMPL(
128
134
 
129
135
  std::weak_ptr<worklets::WorkletRuntime> workletRuntime;
130
136
  auto shouldUseUiRuntime = args[1].getBool();
137
+ auto shouldLockRuntime = shouldUseUiRuntime;
131
138
  if (shouldUseUiRuntime) {
132
139
  workletRuntime = context_->runtimeRegistry_.uiRuntime;
133
140
  } else {
134
141
  workletRuntime = context_->runtimeRegistry_.audioRuntime;
135
142
  }
136
143
 
137
- auto workletProcessingNode =
138
- context_->createWorkletProcessingNode(shareableWorklet, workletRuntime);
144
+ auto workletProcessingNode = context_->createWorkletProcessingNode(
145
+ shareableWorklet, workletRuntime, shouldLockRuntime);
139
146
  auto workletProcessingNodeHostObject =
140
147
  std::make_shared<WorkletProcessingNodeHostObject>(workletProcessingNode);
141
148
  return jsi::Object::createFromHostObject(
@@ -67,9 +67,11 @@ std::shared_ptr<AudioDestinationNode> BaseAudioContext::getDestination() {
67
67
 
68
68
  std::shared_ptr<WorkletSourceNode> BaseAudioContext::createWorkletSourceNode(
69
69
  std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
70
- std::weak_ptr<worklets::WorkletRuntime> runtime) {
70
+ std::weak_ptr<worklets::WorkletRuntime> runtime,
71
+ bool shouldLockRuntime) {
72
+ WorkletsRunner workletRunner(runtime, shareableWorklet, shouldLockRuntime);
71
73
  auto workletSourceNode =
72
- std::make_shared<WorkletSourceNode>(this, shareableWorklet, runtime);
74
+ std::make_shared<WorkletSourceNode>(this, std::move(workletRunner));
73
75
  nodeManager_->addSourceNode(workletSourceNode);
74
76
  return workletSourceNode;
75
77
  }
@@ -78,9 +80,11 @@ std::shared_ptr<WorkletNode> BaseAudioContext::createWorkletNode(
78
80
  std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
79
81
  std::weak_ptr<worklets::WorkletRuntime> runtime,
80
82
  size_t bufferLength,
81
- size_t inputChannelCount) {
83
+ size_t inputChannelCount,
84
+ bool shouldLockRuntime) {
85
+ WorkletsRunner workletRunner(runtime, shareableWorklet, shouldLockRuntime);
82
86
  auto workletNode = std::make_shared<WorkletNode>(
83
- this, shareableWorklet, runtime, bufferLength, inputChannelCount);
87
+ this, bufferLength, inputChannelCount, std::move(workletRunner));
84
88
  nodeManager_->addProcessingNode(workletNode);
85
89
  return workletNode;
86
90
  }
@@ -88,9 +92,11 @@ std::shared_ptr<WorkletNode> BaseAudioContext::createWorkletNode(
88
92
  std::shared_ptr<WorkletProcessingNode>
89
93
  BaseAudioContext::createWorkletProcessingNode(
90
94
  std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
91
- std::weak_ptr<worklets::WorkletRuntime> runtime) {
95
+ std::weak_ptr<worklets::WorkletRuntime> runtime,
96
+ bool shouldLockRuntime) {
97
+ WorkletsRunner workletRunner(runtime, shareableWorklet, shouldLockRuntime);
92
98
  auto workletProcessingNode =
93
- std::make_shared<WorkletProcessingNode>(this, shareableWorklet, runtime);
99
+ std::make_shared<WorkletProcessingNode>(this, std::move(workletRunner));
94
100
  nodeManager_->addProcessingNode(workletProcessingNode);
95
101
  return workletProcessingNode;
96
102
  }
@@ -47,9 +47,20 @@ class BaseAudioContext {
47
47
  std::shared_ptr<AudioDestinationNode> getDestination();
48
48
 
49
49
  std::shared_ptr<RecorderAdapterNode> createRecorderAdapter();
50
- std::shared_ptr<WorkletSourceNode> createWorkletSourceNode(std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet, std::weak_ptr<worklets::WorkletRuntime> runtime);
51
- std::shared_ptr<WorkletNode> createWorkletNode(std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet, std::weak_ptr<worklets::WorkletRuntime> runtime, size_t bufferLength, size_t inputChannelCount);
52
- std::shared_ptr<WorkletProcessingNode> createWorkletProcessingNode(std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet, std::weak_ptr<worklets::WorkletRuntime> runtime);
50
+ std::shared_ptr<WorkletSourceNode> createWorkletSourceNode(
51
+ std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
52
+ std::weak_ptr<worklets::WorkletRuntime> runtime,
53
+ bool shouldLockRuntime = true);
54
+ std::shared_ptr<WorkletNode> createWorkletNode(
55
+ std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
56
+ std::weak_ptr<worklets::WorkletRuntime> runtime,
57
+ size_t bufferLength,
58
+ size_t inputChannelCount,
59
+ bool shouldLockRuntime = true);
60
+ std::shared_ptr<WorkletProcessingNode> createWorkletProcessingNode(
61
+ std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
62
+ std::weak_ptr<worklets::WorkletRuntime> runtime,
63
+ bool shouldLockRuntime = true);
53
64
  std::shared_ptr<OscillatorNode> createOscillator();
54
65
  std::shared_ptr<ConstantSourceNode> createConstantSource();
55
66
  std::shared_ptr<StreamerNode> createStreamer();
@@ -4,13 +4,11 @@ namespace audioapi {
4
4
 
5
5
  WorkletNode::WorkletNode(
6
6
  BaseAudioContext *context,
7
- std::shared_ptr<worklets::SerializableWorklet> &worklet,
8
- std::weak_ptr<worklets::WorkletRuntime> runtime,
9
7
  size_t bufferLength,
10
- size_t inputChannelCount)
8
+ size_t inputChannelCount,
9
+ WorkletsRunner &&runtime)
11
10
  : AudioNode(context),
12
- workletRunner_(runtime),
13
- shareableWorklet_(worklet),
11
+ workletRunner_(std::move(runtime)),
14
12
  bufferLength_(bufferLength),
15
13
  inputChannelCount_(inputChannelCount),
16
14
  curBuffIndex_(0) {
@@ -40,35 +38,36 @@ std::shared_ptr<AudioBus> WorkletNode::processNode(
40
38
  curBuffIndex_ += shouldProcess;
41
39
 
42
40
  /// If we filled the entire buffer, we need to execute the worklet
43
- if (curBuffIndex_ == bufferLength_) {
44
- // Reset buffer index, channel buffers and execute worklet
45
- curBuffIndex_ = 0;
46
- workletRunner_.executeOnRuntimeGuardedSync(
47
- [this, channelCount_](jsi::Runtime &uiRuntimeRaw) {
48
- /// Arguments preparation
49
- auto jsArray = jsi::Array(uiRuntimeRaw, channelCount_);
50
- for (size_t ch = 0; ch < channelCount_; ch++) {
51
- auto audioArray = std::make_shared<AudioArray>(bufferLength_);
52
- audioArray->copy(bus_->getChannel(ch));
53
- auto sharedAudioArray =
54
- std::make_shared<AudioArrayBuffer>(audioArray);
55
- auto sharedAudioArraySize = sharedAudioArray->size();
56
- auto arrayBuffer =
57
- jsi::ArrayBuffer(uiRuntimeRaw, std::move(sharedAudioArray));
58
- arrayBuffer.setExternalMemoryPressure(
59
- uiRuntimeRaw, sharedAudioArraySize);
60
- jsArray.setValueAtIndex(uiRuntimeRaw, ch, std::move(arrayBuffer));
61
- }
41
+ if (curBuffIndex_ != bufferLength_) {
42
+ continue;
43
+ }
44
+ // Reset buffer index, channel buffers and execute worklet
45
+ curBuffIndex_ = 0;
46
+ workletRunner_.executeOnRuntimeSync([this, channelCount_](
47
+ jsi::Runtime &uiRuntimeRaw) {
48
+ /// Arguments preparation
49
+ auto jsArray = jsi::Array(uiRuntimeRaw, channelCount_);
50
+ for (size_t ch = 0; ch < channelCount_; ch++) {
51
+ auto audioArray = std::make_shared<AudioArray>(bufferLength_);
52
+ audioArray->copy(bus_->getChannel(ch));
53
+ auto sharedAudioArray = std::make_shared<AudioArrayBuffer>(audioArray);
54
+ auto sharedAudioArraySize = sharedAudioArray->size();
55
+ auto arrayBuffer =
56
+ jsi::ArrayBuffer(uiRuntimeRaw, std::move(sharedAudioArray));
57
+ arrayBuffer.setExternalMemoryPressure(
58
+ uiRuntimeRaw, sharedAudioArraySize);
59
+ jsArray.setValueAtIndex(uiRuntimeRaw, ch, std::move(arrayBuffer));
60
+ }
62
61
 
63
- bus_->zero();
62
+ bus_->zero();
64
63
 
65
- workletRunner_.executeWorklet(
66
- shareableWorklet_,
67
- std::move(jsArray),
68
- jsi::Value(uiRuntimeRaw, static_cast<int>(channelCount_)));
69
- return jsi::Value::undefined();
70
- });
71
- }
64
+ /// Call the worklet
65
+ workletRunner_.callUnsafe(
66
+ std::move(jsArray),
67
+ jsi::Value(uiRuntimeRaw, static_cast<int>(channelCount_)));
68
+
69
+ return jsi::Value::undefined();
70
+ });
72
71
  }
73
72
 
74
73
  return processingBus;
@@ -19,10 +19,9 @@ class WorkletNode : public AudioNode {
19
19
  public:
20
20
  explicit WorkletNode(
21
21
  BaseAudioContext *context,
22
- std::shared_ptr<worklets::SerializableWorklet> &worklet,
23
- std::weak_ptr<worklets::WorkletRuntime> runtime,
24
22
  size_t bufferLength,
25
- size_t inputChannelCount
23
+ size_t inputChannelCount,
24
+ WorkletsRunner &&workletRunner
26
25
  ) : AudioNode(context) {}
27
26
 
28
27
  protected:
@@ -36,10 +35,9 @@ class WorkletNode : public AudioNode {
36
35
  public:
37
36
  explicit WorkletNode(
38
37
  BaseAudioContext *context,
39
- std::shared_ptr<worklets::SerializableWorklet> &worklet,
40
- std::weak_ptr<worklets::WorkletRuntime> runtime,
41
38
  size_t bufferLength,
42
- size_t inputChannelCount
39
+ size_t inputChannelCount,
40
+ WorkletsRunner &&workletRunner
43
41
  );
44
42
 
45
43
  ~WorkletNode() override = default;
@@ -50,7 +48,6 @@ class WorkletNode : public AudioNode {
50
48
 
51
49
  private:
52
50
  WorkletsRunner workletRunner_;
53
- std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
54
51
  std::shared_ptr<AudioBus> bus_;
55
52
 
56
53
  /// @brief Length of the byte buffer that will be passed to the AudioArrayBuffer
@@ -5,9 +5,8 @@ namespace audioapi {
5
5
 
6
6
  WorkletProcessingNode::WorkletProcessingNode(
7
7
  BaseAudioContext *context,
8
- std::shared_ptr<worklets::SerializableWorklet> &worklet,
9
- std::weak_ptr<worklets::WorkletRuntime> runtime)
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)
@@ -42,8 +41,8 @@ std::shared_ptr<AudioBus> WorkletProcessingNode::processNode(
42
41
  }
43
42
 
44
43
  // Execute the worklet
45
- auto result = workletRunner_.executeOnRuntimeGuardedSync(
46
- [this, channelCount, framesToProcess](jsi::Runtime &rt) {
44
+ auto result = workletRunner_.executeOnRuntimeSync(
45
+ [this, channelCount, framesToProcess](jsi::Runtime &rt) -> jsi::Value {
47
46
  auto inputJsArray = jsi::Array(rt, channelCount);
48
47
  auto outputJsArray = jsi::Array(rt, channelCount);
49
48
 
@@ -58,14 +57,14 @@ std::shared_ptr<AudioBus> WorkletProcessingNode::processNode(
58
57
  outputJsArray.setValueAtIndex(rt, ch, outputArrayBuffer);
59
58
  }
60
59
 
61
- return workletRunner_
62
- .executeWorklet(
63
- shareableWorklet_,
64
- inputJsArray,
65
- outputJsArray,
66
- jsi::Value(rt, static_cast<int>(framesToProcess)),
67
- jsi::Value(rt, this->context_->getCurrentTime()))
68
- .value_or(jsi::Value::undefined());
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()));
69
68
  });
70
69
 
71
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
- std::shared_ptr<worklets::SerializableWorklet> &worklet,
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
- std::shared_ptr<worklets::SerializableWorklet> &worklet,
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
  };
@@ -5,11 +5,9 @@ namespace audioapi {
5
5
 
6
6
  WorkletSourceNode::WorkletSourceNode(
7
7
  BaseAudioContext *context,
8
- std::shared_ptr<worklets::SerializableWorklet> &worklet,
9
- std::weak_ptr<worklets::WorkletRuntime> runtime)
8
+ WorkletsRunner &&workletRunner)
10
9
  : AudioScheduledSourceNode(context),
11
- workletRunner_(runtime),
12
- shareableWorklet_(worklet) {
10
+ workletRunner_(std::move(workletRunner)) {
13
11
  isInitialized_ = true;
14
12
 
15
13
  // Prepare buffers for audio processing
@@ -42,21 +40,22 @@ std::shared_ptr<AudioBus> WorkletSourceNode::processNode(
42
40
 
43
41
  size_t outputChannelCount = processingBus->getNumberOfChannels();
44
42
 
45
- auto result = workletRunner_.executeOnRuntimeGuardedSync(
43
+ auto result = workletRunner_.executeOnRuntimeSync(
46
44
  [this, nonSilentFramesToProcess, startOffset](jsi::Runtime &rt) {
47
45
  auto jsiArray = jsi::Array(rt, this->outputBuffsHandles_.size());
48
46
  for (size_t i = 0; i < this->outputBuffsHandles_.size(); ++i) {
49
47
  auto arrayBuffer = jsi::ArrayBuffer(rt, this->outputBuffsHandles_[i]);
50
48
  jsiArray.setValueAtIndex(rt, i, arrayBuffer);
51
49
  }
52
- return workletRunner_
53
- .executeWorklet(
54
- shareableWorklet_,
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)))
59
- .value_or(jsi::Value::undefined());
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)));
60
59
  });
61
60
 
62
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
- std::shared_ptr<worklets::SerializableWorklet> &worklet,
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
- std::shared_ptr<worklets::SerializableWorklet> &worklet,
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
@@ -27,6 +27,11 @@
27
27
  #include <worklets/android/WorkletsModule.h>
28
28
  #endif
29
29
  #else
30
+
31
+ #define RN_AUDIO_API_WORKLETS_DISABLED_ERROR \
32
+ std::runtime_error( \
33
+ "Worklets are disabled. Please install react-native-worklets or check if you have supported version to enable these features.");
34
+
30
35
  /// @brief Dummy implementation of worklets for non-worklet builds they should do nothing and mock necessary methods
31
36
  /// @note It helps to reduce compile time branching across codebase
32
37
  /// @note If you need to base some c++ implementation on if the worklets are enabled use `#if RN_AUDIO_API_ENABLE_WORKLETS`
@@ -36,17 +41,41 @@ using namespace facebook;
36
41
  class MessageQueueThread {};
37
42
  class WorkletsModuleProxy {};
38
43
  class WorkletRuntime {
39
- explicit WorkletRuntime(uint64_t, const std::shared_ptr<MessageQueueThread> &, const std::string &, const bool);
44
+ public:
45
+ explicit WorkletRuntime(uint64_t, const std::shared_ptr<MessageQueueThread> &, const std::string &, const bool) {
46
+ throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
47
+ }
48
+ jsi::Runtime &getJSIRuntime() const {
49
+ throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
50
+ }
51
+ jsi::Value executeSync(jsi::Runtime &rt, const jsi::Value &worklet) const {
52
+ throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
53
+ }
54
+ jsi::Value executeSync(std::function<jsi::Value(jsi::Runtime &)> &&job) const {
55
+ throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
56
+ }
57
+ jsi::Value executeSync(const std::function<jsi::Value(jsi::Runtime &)> &job) const {
58
+ throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
59
+ }
40
60
  };
41
61
  class SerializableWorklet {
42
- SerializableWorklet(jsi::Runtime*, const jsi::Object &);
62
+ public:
63
+ SerializableWorklet(jsi::Runtime*, const jsi::Object &) {
64
+ throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
65
+ }
66
+ jsi::Value toJSValue(jsi::Runtime &rt) {
67
+ throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR
68
+ }
43
69
  };
44
70
  } // namespace worklets
71
+
72
+ #undef RN_AUDIO_API_WORKLETS_DISABLED_ERROR
73
+
45
74
  #endif
46
75
 
47
76
  /// @brief Struct to hold references to different runtimes used in the AudioAPI
48
77
  /// @note it is used to pass them around and avoid creating multiple instances of the same runtime
49
78
  struct RuntimeRegistry {
50
79
  std::weak_ptr<worklets::WorkletRuntime> uiRuntime;
51
- std::weak_ptr<worklets::WorkletRuntime> audioRuntime;
80
+ std::shared_ptr<worklets::WorkletRuntime> audioRuntime;
52
81
  };
@@ -3,7 +3,80 @@
3
3
  namespace audioapi {
4
4
 
5
5
  WorkletsRunner::WorkletsRunner(
6
- std::weak_ptr<worklets::WorkletRuntime> weakUiRuntime) noexcept
7
- : weakUiRuntime_(std::move(weakUiRuntime)) {}
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
- explicit WorkletsRunner(std::weak_ptr<worklets::WorkletRuntime> weakUiRuntime) noexcept;
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
- /// @brief Execute a job on the UI runtime safely.
32
- /// @param job
33
- /// @return nullopt if the runtime is not available or the result of the job execution
34
- /// @note Execution is synchronous
35
- std::optional<jsi::Value> executeOnRuntimeGuardedSync(const std::function<jsi::Value(jsi::Runtime&)>&& job) const noexcept(noexcept(job)) {
36
- auto strongRuntime = weakUiRuntime_.lock();
37
- if (strongRuntime == nullptr) {
38
- return std::nullopt;
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
- #if RN_AUDIO_API_ENABLE_WORKLETS
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
- return strongRuntime->runGuarded(shareableWorklet, std::forward<Args>(args)...);
63
-
64
- #else
65
- return std::nullopt;
66
- #endif
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
- std::weak_ptr<worklets::WorkletRuntime> weakUiRuntime_;
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
@@ -18,7 +18,8 @@ class IOSAudioRecorder : public AudioRecorder {
18
18
  IOSAudioRecorder(
19
19
  float sampleRate,
20
20
  int bufferLength,
21
- const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);
21
+ const std::shared_ptr<AudioEventHandlerRegistry>
22
+ &audioEventHandlerRegistry);
22
23
 
23
24
  ~IOSAudioRecorder() override;
24
25