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.
Files changed (101) 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/android/src/main/jniLibs/arm64-v8a/libavcodec.so +0 -0
  8. package/android/src/main/jniLibs/arm64-v8a/libavformat.so +0 -0
  9. package/android/src/main/jniLibs/arm64-v8a/libavutil.so +0 -0
  10. package/android/src/main/jniLibs/arm64-v8a/libswresample.so +0 -0
  11. package/android/src/main/jniLibs/armeabi-v7a/libavcodec.so +0 -0
  12. package/android/src/main/jniLibs/armeabi-v7a/libavformat.so +0 -0
  13. package/android/src/main/jniLibs/armeabi-v7a/libavutil.so +0 -0
  14. package/android/src/main/jniLibs/armeabi-v7a/libswresample.so +0 -0
  15. package/android/src/main/jniLibs/x86/libavcodec.so +0 -0
  16. package/android/src/main/jniLibs/x86/libavformat.so +0 -0
  17. package/android/src/main/jniLibs/x86/libavutil.so +0 -0
  18. package/android/src/main/jniLibs/x86/libswresample.so +0 -0
  19. package/android/src/main/jniLibs/x86_64/libavcodec.so +0 -0
  20. package/android/src/main/jniLibs/x86_64/libavformat.so +0 -0
  21. package/android/src/main/jniLibs/x86_64/libavutil.so +0 -0
  22. package/android/src/main/jniLibs/x86_64/libswresample.so +0 -0
  23. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +37 -6
  24. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +12 -5
  25. package/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp +3 -8
  26. package/common/cpp/audioapi/core/BaseAudioContext.cpp +12 -6
  27. package/common/cpp/audioapi/core/BaseAudioContext.h +14 -3
  28. package/common/cpp/audioapi/core/effects/WorkletNode.cpp +39 -52
  29. package/common/cpp/audioapi/core/effects/WorkletNode.h +6 -10
  30. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +17 -19
  31. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +2 -5
  32. package/common/cpp/audioapi/core/sources/AudioBuffer.h +1 -0
  33. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +0 -4
  34. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +0 -1
  35. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +0 -2
  36. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +0 -4
  37. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +0 -1
  38. package/common/cpp/audioapi/core/sources/StreamerNode.cpp +16 -6
  39. package/common/cpp/audioapi/core/sources/StreamerNode.h +3 -1
  40. package/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +14 -16
  41. package/common/cpp/audioapi/core/sources/WorkletSourceNode.h +2 -5
  42. package/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h +32 -13
  43. package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.cpp +75 -2
  44. package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.h +50 -36
  45. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +5 -1
  46. package/common/cpp/audioapi/external/libavcodec.xcframework/ios-arm64/libavcodec.framework/libavcodec +0 -0
  47. package/common/cpp/audioapi/external/libavcodec.xcframework/ios-arm64_x86_64-simulator/libavcodec.framework/libavcodec +0 -0
  48. package/common/cpp/audioapi/external/libavformat.xcframework/Info.plist +5 -5
  49. package/common/cpp/audioapi/external/libavformat.xcframework/ios-arm64/libavformat.framework/libavformat +0 -0
  50. package/common/cpp/audioapi/external/libavformat.xcframework/ios-arm64_x86_64-simulator/libavformat.framework/libavformat +0 -0
  51. package/common/cpp/audioapi/external/libavutil.xcframework/ios-arm64/libavutil.framework/libavutil +0 -0
  52. package/common/cpp/audioapi/external/libavutil.xcframework/ios-arm64_x86_64-simulator/libavutil.framework/libavutil +0 -0
  53. package/common/cpp/audioapi/external/libswresample.xcframework/Info.plist +5 -5
  54. package/common/cpp/audioapi/external/libswresample.xcframework/ios-arm64/libswresample.framework/libswresample +0 -0
  55. package/common/cpp/audioapi/external/libswresample.xcframework/ios-arm64_x86_64-simulator/libswresample.framework/libswresample +0 -0
  56. package/common/cpp/audioapi/jsi/AudioArrayBuffer.cpp +2 -2
  57. package/common/cpp/audioapi/jsi/AudioArrayBuffer.h +10 -11
  58. package/common/cpp/audioapi/libs/ffmpeg/ffmpeg_setup.sh +1 -1
  59. package/common/cpp/audioapi/utils/AudioBus.cpp +4 -0
  60. package/common/cpp/audioapi/utils/AudioBus.h +1 -0
  61. package/common/cpp/test/CMakeLists.txt +7 -4
  62. package/common/cpp/test/RunTests.sh +2 -2
  63. package/common/cpp/test/{AudioParamTest.cpp → src/AudioParamTest.cpp} +1 -1
  64. package/common/cpp/test/src/ConstantSourceTest.cpp +64 -0
  65. package/common/cpp/test/{GainTest.cpp → src/GainTest.cpp} +11 -10
  66. package/common/cpp/test/{MockAudioEventHandlerRegistry.h → src/MockAudioEventHandlerRegistry.h} +4 -2
  67. package/common/cpp/test/{OscillatorTest.cpp → src/OscillatorTest.cpp} +6 -4
  68. package/common/cpp/test/{StereoPannerTest.cpp → src/StereoPannerTest.cpp} +1 -1
  69. package/ios/audioapi/ios/AudioAPIModule.h +2 -1
  70. package/ios/audioapi/ios/AudioAPIModule.mm +2 -0
  71. package/ios/audioapi/ios/core/IOSAudioRecorder.h +2 -1
  72. package/lib/commonjs/core/AudioContext.js +1 -5
  73. package/lib/commonjs/core/AudioContext.js.map +1 -1
  74. package/lib/commonjs/core/BaseAudioContext.js +16 -25
  75. package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
  76. package/lib/commonjs/core/OfflineAudioContext.js +1 -5
  77. package/lib/commonjs/core/OfflineAudioContext.js.map +1 -1
  78. package/lib/commonjs/utils/index.js +20 -4
  79. package/lib/commonjs/utils/index.js.map +1 -1
  80. package/lib/module/core/AudioContext.js +2 -6
  81. package/lib/module/core/AudioContext.js.map +1 -1
  82. package/lib/module/core/BaseAudioContext.js +17 -26
  83. package/lib/module/core/BaseAudioContext.js.map +1 -1
  84. package/lib/module/core/OfflineAudioContext.js +2 -6
  85. package/lib/module/core/OfflineAudioContext.js.map +1 -1
  86. package/lib/module/utils/index.js +16 -1
  87. package/lib/module/utils/index.js.map +1 -1
  88. package/lib/typescript/core/AudioContext.d.ts +0 -1
  89. package/lib/typescript/core/AudioContext.d.ts.map +1 -1
  90. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  91. package/lib/typescript/core/OfflineAudioContext.d.ts +0 -1
  92. package/lib/typescript/core/OfflineAudioContext.d.ts.map +1 -1
  93. package/lib/typescript/utils/index.d.ts +4 -1
  94. package/lib/typescript/utils/index.d.ts.map +1 -1
  95. package/package.json +4 -1
  96. package/scripts/rnaa_utils.rb +8 -0
  97. package/scripts/validate-worklets-version.js +28 -0
  98. package/src/core/AudioContext.ts +3 -7
  99. package/src/core/BaseAudioContext.ts +44 -60
  100. package/src/core/OfflineAudioContext.ts +2 -7
  101. 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(
@@ -1,6 +1,7 @@
1
1
  #include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
2
2
 
3
3
  #include <audioapi/jsi/AudioArrayBuffer.h>
4
+ #include <audioapi/utils/AudioBus.h>
4
5
 
5
6
  namespace audioapi {
6
7
 
@@ -42,14 +43,8 @@ JSI_PROPERTY_GETTER_IMPL(AudioBufferHostObject, numberOfChannels) {
42
43
 
43
44
  JSI_HOST_FUNCTION_IMPL(AudioBufferHostObject, getChannelData) {
44
45
  auto channel = static_cast<int>(args[0].getNumber());
45
- auto channelData =
46
- reinterpret_cast<uint8_t *>(audioBuffer_->getChannelData(channel));
47
- auto length = static_cast<int>(audioBuffer_->getLength());
48
- auto size = static_cast<int>(length * sizeof(float));
49
-
50
- // reading or writing from this ArrayBuffer could cause a crash
51
- // if underlying channelData is deallocated
52
- auto audioArrayBuffer = std::make_shared<AudioArrayBuffer>(channelData, size);
46
+ auto audioArrayBuffer = std::make_shared<AudioArrayBuffer>(
47
+ audioBuffer_->bus_->getSharedChannel(channel));
53
48
  auto arrayBuffer = jsi::ArrayBuffer(runtime, audioArrayBuffer);
54
49
 
55
50
  auto float32ArrayCtor =
@@ -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,30 +4,19 @@ 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
- buffRealLength_(bufferLength * sizeof(float)),
11
+ workletRunner_(std::move(runtime)),
13
12
  bufferLength_(bufferLength),
14
- workletRunner_(runtime),
15
- shareableWorklet_(worklet),
16
13
  inputChannelCount_(inputChannelCount),
17
14
  curBuffIndex_(0) {
18
- buffs_.reserve(inputChannelCount_);
19
- for (size_t i = 0; i < inputChannelCount_; ++i) {
20
- buffs_.emplace_back(new uint8_t[buffRealLength_]);
21
- }
15
+ bus_ = std::make_shared<AudioBus>(
16
+ bufferLength, inputChannelCount, context->getSampleRate());
22
17
  isInitialized_ = true;
23
18
  }
24
19
 
25
- WorkletNode::~WorkletNode() {
26
- for (auto &buff : buffs_) {
27
- delete[] buff;
28
- }
29
- }
30
-
31
20
  std::shared_ptr<AudioBus> WorkletNode::processNode(
32
21
  const std::shared_ptr<AudioBus> &processingBus,
33
22
  int framesToProcess) {
@@ -40,47 +29,45 @@ std::shared_ptr<AudioBus> WorkletNode::processNode(
40
29
  size_t needsToProcess = framesToProcess - processed;
41
30
  size_t shouldProcess = std::min(framesToWorkletInvoke, needsToProcess);
42
31
 
43
- for (size_t ch = 0; ch < channelCount_; ch++) {
44
- /// here we copy
45
- /// to uint8_t* [curBuffIndex_, curBuffIndex_ + shouldProcess]
46
- /// from float* [processed, processed + shouldProcess]
47
- /// so as the we need to copy shouldProcess * sizeof(float) bytes
48
- auto channelData = processingBus->getChannel(ch)->getData();
49
- std::memcpy(
50
- /* dest */ buffs_[ch] + curBuffIndex_ * sizeof(float),
51
- /* src */ reinterpret_cast<const uint8_t *>(channelData + processed),
52
- /* size */ shouldProcess * sizeof(float));
53
- }
32
+ /// here we copy
33
+ /// to [curBuffIndex_, curBuffIndex_ + shouldProcess]
34
+ /// from [processed, processed + shouldProcess]
35
+ bus_->copy(processingBus.get(), processed, curBuffIndex_, shouldProcess);
36
+
54
37
  processed += shouldProcess;
55
38
  curBuffIndex_ += shouldProcess;
56
39
 
57
40
  /// If we filled the entire buffer, we need to execute the worklet
58
- if (curBuffIndex_ == bufferLength_) {
59
- // Reset buffer index, channel buffers and execute worklet
60
- curBuffIndex_ = 0;
61
- workletRunner_.executeOnRuntimeGuardedSync(
62
- [this, channelCount_](jsi::Runtime &uiRuntimeRaw) {
63
- /// Arguments preparation
64
- auto jsArray = jsi::Array(uiRuntimeRaw, channelCount_);
65
- for (size_t ch = 0; ch < channelCount_; ch++) {
66
- uint8_t *buffPtr = buffs_[ch];
67
- buffs_[ch] = new uint8_t[buffRealLength_];
68
- auto sharedAudioArray =
69
- std::make_shared<AudioArrayBuffer>(buffPtr, buffRealLength_);
70
- auto arrayBuffer =
71
- jsi::ArrayBuffer(uiRuntimeRaw, std::move(sharedAudioArray));
72
- jsArray.setValueAtIndex(uiRuntimeRaw, ch, std::move(arrayBuffer));
73
- }
74
- jsArray.setExternalMemoryPressure(
75
- uiRuntimeRaw, channelCount_ * buffRealLength_);
76
-
77
- workletRunner_.executeWorklet(
78
- shareableWorklet_,
79
- std::move(jsArray),
80
- jsi::Value(uiRuntimeRaw, static_cast<int>(channelCount_)));
81
- return jsi::Value::undefined();
82
- });
41
+ if (curBuffIndex_ != bufferLength_) {
42
+ continue;
83
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
+ }
61
+
62
+ bus_->zero();
63
+
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
+ });
84
71
  }
85
72
 
86
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,13 +35,12 @@ 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
- ~WorkletNode() override;
43
+ ~WorkletNode() override = default;
46
44
 
47
45
  protected:
48
46
  std::shared_ptr<AudioBus> processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
@@ -50,11 +48,9 @@ class WorkletNode : public AudioNode {
50
48
 
51
49
  private:
52
50
  WorkletsRunner workletRunner_;
53
- std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
54
- std::vector<uint8_t*> buffs_;
51
+ std::shared_ptr<AudioBus> bus_;
55
52
 
56
53
  /// @brief Length of the byte buffer that will be passed to the AudioArrayBuffer
57
- size_t buffRealLength_;
58
54
  size_t bufferLength_;
59
55
  size_t inputChannelCount_;
60
56
  size_t curBuffIndex_;