react-native-audio-api 0.9.1 → 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.
Files changed (52) hide show
  1. package/RNAudioAPI.podspec +5 -2
  2. package/android/build.gradle +26 -2
  3. package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +5 -0
  4. package/android/src/main/cpp/audioapi/android/AudioAPIModule.h +1 -0
  5. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +4 -2
  6. package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +29 -1
  7. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +37 -6
  8. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +12 -5
  9. package/common/cpp/audioapi/core/BaseAudioContext.cpp +12 -6
  10. package/common/cpp/audioapi/core/BaseAudioContext.h +14 -3
  11. package/common/cpp/audioapi/core/effects/WorkletNode.cpp +31 -32
  12. package/common/cpp/audioapi/core/effects/WorkletNode.h +4 -7
  13. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +12 -13
  14. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +2 -5
  15. package/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +12 -13
  16. package/common/cpp/audioapi/core/sources/WorkletSourceNode.h +2 -5
  17. package/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h +32 -13
  18. package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.cpp +75 -2
  19. package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.h +50 -36
  20. package/ios/audioapi/ios/AudioAPIModule.h +2 -1
  21. package/ios/audioapi/ios/AudioAPIModule.mm +2 -0
  22. package/ios/audioapi/ios/core/IOSAudioRecorder.h +2 -1
  23. package/lib/commonjs/core/AudioContext.js +1 -5
  24. package/lib/commonjs/core/AudioContext.js.map +1 -1
  25. package/lib/commonjs/core/BaseAudioContext.js +16 -25
  26. package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
  27. package/lib/commonjs/core/OfflineAudioContext.js +1 -5
  28. package/lib/commonjs/core/OfflineAudioContext.js.map +1 -1
  29. package/lib/commonjs/utils/index.js +20 -4
  30. package/lib/commonjs/utils/index.js.map +1 -1
  31. package/lib/module/core/AudioContext.js +2 -6
  32. package/lib/module/core/AudioContext.js.map +1 -1
  33. package/lib/module/core/BaseAudioContext.js +17 -26
  34. package/lib/module/core/BaseAudioContext.js.map +1 -1
  35. package/lib/module/core/OfflineAudioContext.js +2 -6
  36. package/lib/module/core/OfflineAudioContext.js.map +1 -1
  37. package/lib/module/utils/index.js +16 -1
  38. package/lib/module/utils/index.js.map +1 -1
  39. package/lib/typescript/core/AudioContext.d.ts +0 -1
  40. package/lib/typescript/core/AudioContext.d.ts.map +1 -1
  41. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  42. package/lib/typescript/core/OfflineAudioContext.d.ts +0 -1
  43. package/lib/typescript/core/OfflineAudioContext.d.ts.map +1 -1
  44. package/lib/typescript/utils/index.d.ts +4 -1
  45. package/lib/typescript/utils/index.d.ts.map +1 -1
  46. package/package.json +4 -1
  47. package/scripts/rnaa_utils.rb +8 -0
  48. package/scripts/validate-worklets-version.js +28 -0
  49. package/src/core/AudioContext.ts +3 -7
  50. package/src/core/BaseAudioContext.ts +44 -60
  51. package/src/core/OfflineAudioContext.ts +2 -7
  52. package/src/utils/index.ts +23 -1
@@ -1,4 +1,5 @@
1
1
  require "json"
2
+ require_relative './scripts/rnaa_utils'
2
3
 
3
4
  package_json = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
5
 
@@ -8,6 +9,8 @@ folly_flags = "-DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-
8
9
  fabric_flags = $new_arch_enabled ? '-DRCT_NEW_ARCH_ENABLED' : ''
9
10
  version_flag = "-DAUDIOAPI_VERSION=#{package_json['version']}"
10
11
 
12
+ worklets_preprocessor_flag = check_if_worklets_enabled() ? '-DRN_AUDIO_API_ENABLE_WORKLETS=1' : ''
13
+
11
14
  Pod::Spec.new do |s|
12
15
  s.name = "RNAudioAPI"
13
16
  s.version = package_json["version"]
@@ -68,8 +71,8 @@ s.pod_target_xcconfig = {
68
71
  $(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include/vorbis
69
72
  $(PODS_TARGET_SRCROOT)/#{external_dir_relative}/ffmpeg_include
70
73
  ].join(" "),
71
- 'OTHER_CFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag}",
72
- 'OTHER_CPLUSPLUSFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag}"
74
+ 'OTHER_CFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag}",
75
+ 'OTHER_CPLUSPLUSFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag}",
73
76
  }
74
77
 
75
78
  s.user_target_xcconfig = {
@@ -74,6 +74,30 @@ def resolveReactNativeWorkletsDirectory() {
74
74
  return null;
75
75
  }
76
76
 
77
+ def validateWorkletsVersion() {
78
+ def validationScript = file("${projectDir}/../scripts/validate-worklets-version.js")
79
+ if (!validationScript.exists()) {
80
+ logger.error("[AudioAPI] Worklets validation script not found at ${validationScript.absolutePath}")
81
+ return false
82
+ }
83
+
84
+ try {
85
+ def process = ["node", validationScript.absolutePath].execute()
86
+ process.waitForProcessOutput(System.out, System.err)
87
+ def exitCode = process.exitValue()
88
+
89
+ if (exitCode == 0) {
90
+ return true
91
+ } else {
92
+ logger.warn("[AudioAPI] Worklets version validation failed")
93
+ return false
94
+ }
95
+ } catch (Exception e) {
96
+ logger.error("[AudioAPI] Failed to validate worklets version: ${e.message}")
97
+ return false
98
+ }
99
+ }
100
+
77
101
  def toPlatformFileString(String path) {
78
102
  if (Os.isFamily(Os.FAMILY_WINDOWS)) {
79
103
  path = path.replace(File.separatorChar, '/' as char)
@@ -92,7 +116,7 @@ static def supportsNamespace() {
92
116
 
93
117
  def reactNativeRootDir = resolveReactNativeDirectory()
94
118
  def reactNativeWorkletsRootDir = resolveReactNativeWorkletsDirectory()
95
- def isWorkletsAvailable = reactNativeWorkletsRootDir != null
119
+ def isWorkletsAvailable = reactNativeWorkletsRootDir != null && validateWorkletsVersion()
96
120
 
97
121
  def reactProperties = new Properties()
98
122
  file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
@@ -123,7 +147,7 @@ android {
123
147
  defaultConfig {
124
148
  minSdkVersion getExtOrIntegerDefault("minSdkVersion")
125
149
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
126
-
150
+
127
151
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
128
152
  buildConfigField "boolean", "RN_AUDIO_API_ENABLE_WORKLETS", "${isWorkletsAvailable}"
129
153
 
@@ -51,6 +51,7 @@ void AudioAPIModule::registerNatives() {
51
51
  makeNativeMethod(
52
52
  "invokeHandlerWithEventNameAndEventBody",
53
53
  AudioAPIModule::invokeHandlerWithEventNameAndEventBody),
54
+ makeNativeMethod("closeAllContexts", AudioAPIModule::closeAllContexts),
54
55
  });
55
56
  }
56
57
 
@@ -101,4 +102,8 @@ void AudioAPIModule::invokeHandlerWithEventNameAndEventBody(
101
102
  eventName->toStdString(), body);
102
103
  }
103
104
  }
105
+
106
+ void AudioAPIModule::closeAllContexts() {
107
+ AudioAPIModuleInstaller::closeAllContexts();
108
+ }
104
109
  } // namespace audioapi
@@ -34,6 +34,7 @@ class AudioAPIModule : public jni::HybridClass<AudioAPIModule> {
34
34
 
35
35
  void injectJSIBindings();
36
36
  void invokeHandlerWithEventNameAndEventBody(jni::alias_ref<jni::JString> eventName, jni::alias_ref<jni::JMap<jstring, jobject>> eventBody);
37
+ void closeAllContexts();
37
38
 
38
39
  private:
39
40
  friend HybridBase;
@@ -4,6 +4,7 @@
4
4
  #include <audioapi/core/utils/Constants.h>
5
5
  #include <audioapi/utils/AudioArray.h>
6
6
  #include <audioapi/utils/AudioBus.h>
7
+ #include <jni.h>
7
8
 
8
9
  namespace audioapi {
9
10
 
@@ -49,7 +50,8 @@ bool AudioPlayer::openAudioStream() {
49
50
 
50
51
  bool AudioPlayer::start() {
51
52
  if (mStream_) {
52
- nativeAudioPlayer_->start();
53
+ jni::ThreadScope::WithClassLoader(
54
+ [this]() { nativeAudioPlayer_->start(); });
53
55
  auto result = mStream_->requestStart();
54
56
  return result == oboe::Result::OK;
55
57
  }
@@ -59,7 +61,7 @@ bool AudioPlayer::start() {
59
61
 
60
62
  void AudioPlayer::stop() {
61
63
  if (mStream_) {
62
- nativeAudioPlayer_->stop();
64
+ jni::ThreadScope::WithClassLoader([this]() { nativeAudioPlayer_->stop(); });
63
65
  mStream_->requestStop();
64
66
  }
65
67
  }
@@ -1,6 +1,8 @@
1
1
  package com.swmansion.audioapi
2
2
 
3
3
  import com.facebook.jni.HybridData
4
+ import com.facebook.react.bridge.LifecycleEventListener
5
+ import com.facebook.react.bridge.NativeModule
4
6
  import com.facebook.react.bridge.Promise
5
7
  import com.facebook.react.bridge.ReactApplicationContext
6
8
  import com.facebook.react.bridge.ReadableArray
@@ -16,7 +18,8 @@ import java.lang.ref.WeakReference
16
18
  @ReactModule(name = AudioAPIModule.NAME)
17
19
  class AudioAPIModule(
18
20
  reactContext: ReactApplicationContext,
19
- ) : NativeAudioAPIModuleSpec(reactContext) {
21
+ ) : NativeAudioAPIModuleSpec(reactContext),
22
+ LifecycleEventListener {
20
23
  companion object {
21
24
  const val NAME = NativeAudioAPIModuleSpec.NAME
22
25
  }
@@ -24,6 +27,7 @@ class AudioAPIModule(
24
27
  val reactContext: WeakReference<ReactApplicationContext> = WeakReference(reactContext)
25
28
 
26
29
  private val mHybridData: HybridData
30
+ private var reanimatedModule: NativeModule? = null
27
31
 
28
32
  external fun initHybrid(
29
33
  workletsModule: Any?,
@@ -38,6 +42,8 @@ class AudioAPIModule(
38
42
  eventBody: Map<String, Any>,
39
43
  )
40
44
 
45
+ private external fun closeAllContexts()
46
+
41
47
  init {
42
48
  try {
43
49
  System.loadLibrary("react-native-audio-api")
@@ -47,6 +53,8 @@ class AudioAPIModule(
47
53
  if (BuildConfig.RN_AUDIO_API_ENABLE_WORKLETS) {
48
54
  try {
49
55
  workletsModule = reactContext.getNativeModule("WorkletsModule")
56
+ reanimatedModule = reactContext.getNativeModule("ReanimatedModule")
57
+ reanimatedModule
50
58
  } catch (ex: Exception) {
51
59
  throw RuntimeException("WorkletsModule not found - make sure react-native-worklets is properly installed")
52
60
  }
@@ -64,6 +72,26 @@ class AudioAPIModule(
64
72
  return true
65
73
  }
66
74
 
75
+ override fun onHostResume() {
76
+ // do nothing
77
+ }
78
+
79
+ override fun onHostPause() {
80
+ closeAllContexts()
81
+ }
82
+
83
+ override fun onHostDestroy() {
84
+ // do nothing
85
+ }
86
+
87
+ override fun initialize() {
88
+ reactContext.get()?.addLifecycleEventListener(this)
89
+ }
90
+
91
+ override fun invalidate() {
92
+ // think about cleaning up resources, singletons etc.
93
+ }
94
+
67
95
  override fun getDevicePreferredSampleRate(): Double = MediaSessionManager.getDevicePreferredSampleRate()
68
96
 
69
97
  override fun setAudioSessionActivity(
@@ -14,12 +14,16 @@
14
14
  #include <audioapi/core/utils/worklets/SafeIncludes.h>
15
15
 
16
16
  #include <memory>
17
+ #include <vector>
17
18
 
18
19
  namespace audioapi {
19
20
 
20
21
  using namespace facebook;
21
22
 
22
23
  class AudioAPIModuleInstaller {
24
+ private:
25
+ inline static std::vector<std::weak_ptr<AudioContext>> contexts_ = {};
26
+
23
27
  public:
24
28
  static void injectJSIBindings(
25
29
  jsi::Runtime *jsiRuntime,
@@ -39,6 +43,19 @@ class AudioAPIModuleInstaller {
39
43
  jsiRuntime->global().setProperty(*jsiRuntime, "AudioEventEmitter", jsi::Object::createFromHostObject(*jsiRuntime, audioEventHandlerRegistryHostObject));
40
44
  }
41
45
 
46
+ static void closeAllContexts() {
47
+ for (auto it = contexts_.begin(); it != contexts_.end(); ++it) {
48
+ auto weakContext = *it;
49
+
50
+ if (auto context = weakContext.lock()) {
51
+ context->close();
52
+ }
53
+
54
+ it = contexts_.erase(it);
55
+ --it;
56
+ }
57
+ }
58
+
42
59
  private:
43
60
  static jsi::Function getCreateAudioContextFunction(
44
61
  jsi::Runtime *jsiRuntime,
@@ -67,9 +84,16 @@ class AudioAPIModuleInstaller {
67
84
  auto runtimeRegistry = RuntimeRegistry{};
68
85
  #endif
69
86
 
70
- audioContext = std::make_shared<AudioContext>(sampleRate, initSuspended, audioEventHandlerRegistry, runtimeRegistry);
71
- auto audioContextHostObject = std::make_shared<AudioContextHostObject>(
72
- audioContext, &runtime, jsCallInvoker);
87
+ audioContext = std::make_shared<AudioContext>(
88
+ sampleRate,
89
+ initSuspended,
90
+ audioEventHandlerRegistry,
91
+ runtimeRegistry);
92
+ AudioAPIModuleInstaller::contexts_.push_back(audioContext);
93
+
94
+ auto audioContextHostObject =
95
+ std::make_shared<AudioContextHostObject>(
96
+ audioContext, &runtime, jsCallInvoker);
73
97
 
74
98
  return jsi::Object::createFromHostObject(
75
99
  runtime, audioContextHostObject);
@@ -103,9 +127,16 @@ class AudioAPIModuleInstaller {
103
127
  auto runtimeRegistry = RuntimeRegistry{};
104
128
  #endif
105
129
 
106
- auto offlineAudioContext = std::make_shared<OfflineAudioContext>(numberOfChannels, length, sampleRate, audioEventHandlerRegistry, runtimeRegistry);
107
- auto audioContextHostObject = std::make_shared<OfflineAudioContextHostObject>(
108
- offlineAudioContext, &runtime, jsCallInvoker);
130
+ auto offlineAudioContext = std::make_shared<OfflineAudioContext>(
131
+ numberOfChannels,
132
+ length,
133
+ sampleRate,
134
+ audioEventHandlerRegistry,
135
+ runtimeRegistry);
136
+
137
+ auto audioContextHostObject =
138
+ std::make_shared<OfflineAudioContextHostObject>(
139
+ offlineAudioContext, &runtime, jsCallInvoker);
109
140
 
110
141
  return jsi::Object::createFromHostObject(
111
142
  runtime, audioContextHostObject);
@@ -81,14 +81,15 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWorkletSourceNode) {
81
81
  runtime, args[0]);
82
82
  std::weak_ptr<worklets::WorkletRuntime> workletRuntime;
83
83
  auto shouldUseUiRuntime = args[1].getBool();
84
+ auto shouldLockRuntime = shouldUseUiRuntime;
84
85
  if (shouldUseUiRuntime) {
85
86
  workletRuntime = context_->runtimeRegistry_.uiRuntime;
86
87
  } else {
87
88
  workletRuntime = context_->runtimeRegistry_.audioRuntime;
88
89
  }
89
90
 
90
- auto workletSourceNode =
91
- context_->createWorkletSourceNode(shareableWorklet, workletRuntime);
91
+ auto workletSourceNode = context_->createWorkletSourceNode(
92
+ shareableWorklet, workletRuntime, shouldLockRuntime);
92
93
  auto workletSourceNodeHostObject =
93
94
  std::make_shared<WorkletSourceNodeHostObject>(workletSourceNode);
94
95
  return jsi::Object::createFromHostObject(
@@ -105,6 +106,7 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWorkletNode) {
105
106
 
106
107
  std::weak_ptr<worklets::WorkletRuntime> workletRuntime;
107
108
  auto shouldUseUiRuntime = args[1].getBool();
109
+ auto shouldLockRuntime = shouldUseUiRuntime;
108
110
  if (shouldUseUiRuntime) {
109
111
  workletRuntime = context_->runtimeRegistry_.uiRuntime;
110
112
  } else {
@@ -114,7 +116,11 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWorkletNode) {
114
116
  auto inputChannelCount = static_cast<size_t>(args[3].getNumber());
115
117
 
116
118
  auto workletNode = context_->createWorkletNode(
117
- shareableWorklet, workletRuntime, bufferLength, inputChannelCount);
119
+ shareableWorklet,
120
+ workletRuntime,
121
+ bufferLength,
122
+ inputChannelCount,
123
+ shouldLockRuntime);
118
124
  auto workletNodeHostObject =
119
125
  std::make_shared<WorkletNodeHostObject>(workletNode);
120
126
  return jsi::Object::createFromHostObject(runtime, workletNodeHostObject);
@@ -132,14 +138,15 @@ JSI_HOST_FUNCTION_IMPL(
132
138
 
133
139
  std::weak_ptr<worklets::WorkletRuntime> workletRuntime;
134
140
  auto shouldUseUiRuntime = args[1].getBool();
141
+ auto shouldLockRuntime = shouldUseUiRuntime;
135
142
  if (shouldUseUiRuntime) {
136
143
  workletRuntime = context_->runtimeRegistry_.uiRuntime;
137
144
  } else {
138
145
  workletRuntime = context_->runtimeRegistry_.audioRuntime;
139
146
  }
140
147
 
141
- auto workletProcessingNode =
142
- context_->createWorkletProcessingNode(shareableWorklet, workletRuntime);
148
+ auto workletProcessingNode = context_->createWorkletProcessingNode(
149
+ shareableWorklet, workletRuntime, shouldLockRuntime);
143
150
  auto workletProcessingNodeHostObject =
144
151
  std::make_shared<WorkletProcessingNodeHostObject>(workletProcessingNode);
145
152
  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
  }
@@ -49,9 +49,20 @@ class BaseAudioContext {
49
49
  std::shared_ptr<AudioDestinationNode> getDestination();
50
50
 
51
51
  std::shared_ptr<RecorderAdapterNode> createRecorderAdapter();
52
- std::shared_ptr<WorkletSourceNode> createWorkletSourceNode(std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet, std::weak_ptr<worklets::WorkletRuntime> runtime);
53
- std::shared_ptr<WorkletNode> createWorkletNode(std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet, std::weak_ptr<worklets::WorkletRuntime> runtime, size_t bufferLength, size_t inputChannelCount);
54
- std::shared_ptr<WorkletProcessingNode> createWorkletProcessingNode(std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet, std::weak_ptr<worklets::WorkletRuntime> runtime);
52
+ std::shared_ptr<WorkletSourceNode> createWorkletSourceNode(
53
+ std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
54
+ std::weak_ptr<worklets::WorkletRuntime> runtime,
55
+ bool shouldLockRuntime = true);
56
+ std::shared_ptr<WorkletNode> createWorkletNode(
57
+ std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
58
+ std::weak_ptr<worklets::WorkletRuntime> runtime,
59
+ size_t bufferLength,
60
+ size_t inputChannelCount,
61
+ bool shouldLockRuntime = true);
62
+ std::shared_ptr<WorkletProcessingNode> createWorkletProcessingNode(
63
+ std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
64
+ std::weak_ptr<worklets::WorkletRuntime> runtime,
65
+ bool shouldLockRuntime = true);
55
66
  std::shared_ptr<OscillatorNode> createOscillator();
56
67
  std::shared_ptr<ConstantSourceNode> createConstantSource();
57
68
  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