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.
- package/RNAudioAPI.podspec +5 -2
- package/android/build.gradle +26 -2
- package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +5 -0
- package/android/src/main/cpp/audioapi/android/AudioAPIModule.h +1 -0
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +4 -2
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +29 -1
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +37 -6
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +12 -5
- package/common/cpp/audioapi/core/BaseAudioContext.cpp +12 -6
- package/common/cpp/audioapi/core/BaseAudioContext.h +14 -3
- package/common/cpp/audioapi/core/effects/WorkletNode.cpp +31 -32
- package/common/cpp/audioapi/core/effects/WorkletNode.h +4 -7
- package/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +12 -13
- package/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +2 -5
- package/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +12 -13
- package/common/cpp/audioapi/core/sources/WorkletSourceNode.h +2 -5
- package/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h +32 -13
- package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.cpp +75 -2
- package/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.h +50 -36
- package/ios/audioapi/ios/AudioAPIModule.h +2 -1
- package/ios/audioapi/ios/AudioAPIModule.mm +2 -0
- package/ios/audioapi/ios/core/IOSAudioRecorder.h +2 -1
- package/lib/commonjs/core/AudioContext.js +1 -5
- package/lib/commonjs/core/AudioContext.js.map +1 -1
- package/lib/commonjs/core/BaseAudioContext.js +16 -25
- package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
- package/lib/commonjs/core/OfflineAudioContext.js +1 -5
- package/lib/commonjs/core/OfflineAudioContext.js.map +1 -1
- package/lib/commonjs/utils/index.js +20 -4
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/module/core/AudioContext.js +2 -6
- package/lib/module/core/AudioContext.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +17 -26
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/module/core/OfflineAudioContext.js +2 -6
- package/lib/module/core/OfflineAudioContext.js.map +1 -1
- package/lib/module/utils/index.js +16 -1
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/core/AudioContext.d.ts +0 -1
- package/lib/typescript/core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/core/OfflineAudioContext.d.ts +0 -1
- package/lib/typescript/core/OfflineAudioContext.d.ts.map +1 -1
- package/lib/typescript/utils/index.d.ts +4 -1
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/package.json +4 -1
- package/scripts/rnaa_utils.rb +8 -0
- package/scripts/validate-worklets-version.js +28 -0
- package/src/core/AudioContext.ts +3 -7
- package/src/core/BaseAudioContext.ts +44 -60
- package/src/core/OfflineAudioContext.ts +2 -7
- package/src/utils/index.ts +23 -1
package/RNAudioAPI.podspec
CHANGED
|
@@ -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 = {
|
package/android/build.gradle
CHANGED
|
@@ -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
|
-
|
|
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>(
|
|
71
|
-
|
|
72
|
-
|
|
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>(
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
53
|
-
|
|
54
|
-
|
|
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_
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
62
|
+
bus_->zero();
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
9
|
-
std::
|
|
10
|
-
: AudioNode(context), workletRunner_(runtime), shareableWorklet_(worklet) {
|
|
8
|
+
WorkletsRunner &&workletRunner)
|
|
9
|
+
: AudioNode(context), workletRunner_(std::move(workletRunner)) {
|
|
11
10
|
isInitialized_ = true;
|
|
12
11
|
|
|
13
12
|
// Pre-allocate buffers for max 128 frames and 2 channels (stereo)
|
|
@@ -42,8 +41,8 @@ std::shared_ptr<AudioBus> WorkletProcessingNode::processNode(
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
// Execute the worklet
|
|
45
|
-
auto result = workletRunner_.
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
22
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime
|
|
21
|
+
WorkletsRunner &&workletRunner
|
|
23
22
|
) : AudioNode(context) {}
|
|
24
23
|
|
|
25
24
|
protected:
|
|
@@ -33,8 +32,7 @@ class WorkletProcessingNode : public AudioNode {
|
|
|
33
32
|
public:
|
|
34
33
|
explicit WorkletProcessingNode(
|
|
35
34
|
BaseAudioContext *context,
|
|
36
|
-
|
|
37
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime
|
|
35
|
+
WorkletsRunner &&workletRunner
|
|
38
36
|
);
|
|
39
37
|
|
|
40
38
|
protected:
|
|
@@ -42,7 +40,6 @@ class WorkletProcessingNode : public AudioNode {
|
|
|
42
40
|
|
|
43
41
|
private:
|
|
44
42
|
WorkletsRunner workletRunner_;
|
|
45
|
-
std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
|
|
46
43
|
std::vector<std::shared_ptr<AudioArrayBuffer>> inputBuffsHandles_;
|
|
47
44
|
std::vector<std::shared_ptr<AudioArrayBuffer>> outputBuffsHandles_;
|
|
48
45
|
};
|
|
@@ -5,11 +5,9 @@ namespace audioapi {
|
|
|
5
5
|
|
|
6
6
|
WorkletSourceNode::WorkletSourceNode(
|
|
7
7
|
BaseAudioContext *context,
|
|
8
|
-
|
|
9
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime)
|
|
8
|
+
WorkletsRunner &&workletRunner)
|
|
10
9
|
: AudioScheduledSourceNode(context),
|
|
11
|
-
workletRunner_(
|
|
12
|
-
shareableWorklet_(worklet) {
|
|
10
|
+
workletRunner_(std::move(workletRunner)) {
|
|
13
11
|
isInitialized_ = true;
|
|
14
12
|
|
|
15
13
|
// Prepare buffers for audio processing
|
|
@@ -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_.
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
22
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime
|
|
21
|
+
WorkletsRunner &&workletRunner
|
|
23
22
|
) : AudioScheduledSourceNode(context) {}
|
|
24
23
|
|
|
25
24
|
protected:
|
|
@@ -31,15 +30,13 @@ class WorkletSourceNode : public AudioScheduledSourceNode {
|
|
|
31
30
|
public:
|
|
32
31
|
explicit WorkletSourceNode(
|
|
33
32
|
BaseAudioContext *context,
|
|
34
|
-
|
|
35
|
-
std::weak_ptr<worklets::WorkletRuntime> runtime
|
|
33
|
+
WorkletsRunner &&workletRunner
|
|
36
34
|
);
|
|
37
35
|
|
|
38
36
|
protected:
|
|
39
37
|
std::shared_ptr<AudioBus> processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
|
|
40
38
|
private:
|
|
41
39
|
WorkletsRunner workletRunner_;
|
|
42
|
-
std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
|
|
43
40
|
std::vector<std::shared_ptr<AudioArrayBuffer>> outputBuffsHandles_;
|
|
44
41
|
};
|
|
45
42
|
#endif // RN_AUDIO_API_TEST
|