react-native-audio-api 0.8.3-nightly-245398c-20250924 → 0.8.3-nightly-2295d0d-20250926

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 (136) hide show
  1. package/RNAudioAPI.podspec +8 -5
  2. package/android/build.gradle +44 -4
  3. package/android/src/main/cpp/audioapi/CMakeLists.txt +65 -0
  4. package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +29 -1
  5. package/android/src/main/cpp/audioapi/android/AudioAPIModule.h +14 -0
  6. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +7 -1
  7. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +6 -1
  8. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +1 -1
  9. package/android/src/main/cpp/audioapi/android/core/NativeAudioRecorder.hpp +36 -0
  10. package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +11 -1
  11. package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioRecorder.kt +24 -0
  12. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +15 -2
  13. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +31 -13
  14. package/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp +57 -0
  15. package/common/cpp/audioapi/HostObjects/AudioContextHostObject.h +6 -46
  16. package/common/cpp/audioapi/HostObjects/AudioNodeHostObject.cpp +70 -6
  17. package/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h +10 -66
  18. package/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp +105 -0
  19. package/common/cpp/audioapi/HostObjects/AudioParamHostObject.h +17 -91
  20. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +292 -6
  21. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +26 -242
  22. package/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.cpp +70 -0
  23. package/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.h +6 -50
  24. package/common/cpp/audioapi/HostObjects/WorkletNodeHostObject.h +18 -0
  25. package/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp +148 -0
  26. package/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h +37 -0
  27. package/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp +92 -0
  28. package/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h +29 -0
  29. package/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.cpp +20 -0
  30. package/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h +19 -0
  31. package/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.cpp +21 -0
  32. package/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h +21 -0
  33. package/common/cpp/audioapi/HostObjects/events/AudioEventHandlerRegistryHostObject.cpp +41 -0
  34. package/common/cpp/audioapi/HostObjects/events/AudioEventHandlerRegistryHostObject.h +28 -0
  35. package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +69 -0
  36. package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.h +33 -0
  37. package/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp +73 -0
  38. package/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h +29 -0
  39. package/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.cpp +94 -0
  40. package/common/cpp/audioapi/HostObjects/sources/AudioBufferHostObject.h +46 -0
  41. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +60 -0
  42. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +25 -0
  43. package/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp +133 -0
  44. package/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.h +34 -0
  45. package/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.cpp +52 -0
  46. package/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.h +25 -0
  47. package/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp +55 -0
  48. package/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.h +27 -0
  49. package/common/cpp/audioapi/HostObjects/{RecorderAdapterNodeHostObject.h → sources/RecorderAdapterNodeHostObject.h} +1 -2
  50. package/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.cpp +22 -0
  51. package/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h +28 -0
  52. package/common/cpp/audioapi/core/AudioContext.cpp +3 -2
  53. package/common/cpp/audioapi/core/AudioContext.h +2 -1
  54. package/common/cpp/audioapi/core/AudioNode.h +1 -1
  55. package/common/cpp/audioapi/core/AudioParam.h +1 -1
  56. package/common/cpp/audioapi/core/BaseAudioContext.cpp +15 -1
  57. package/common/cpp/audioapi/core/BaseAudioContext.h +7 -3
  58. package/common/cpp/audioapi/core/OfflineAudioContext.cpp +4 -3
  59. package/common/cpp/audioapi/core/OfflineAudioContext.h +2 -1
  60. package/common/cpp/audioapi/core/effects/PeriodicWave.cpp +1 -1
  61. package/common/cpp/audioapi/core/effects/StereoPannerNode.cpp +1 -1
  62. package/common/cpp/audioapi/core/effects/WorkletNode.cpp +86 -0
  63. package/common/cpp/audioapi/core/effects/WorkletNode.h +64 -0
  64. package/common/cpp/audioapi/core/inputs/AudioRecorder.cpp +1 -1
  65. package/common/cpp/audioapi/core/inputs/AudioRecorder.h +2 -2
  66. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +1 -1
  67. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +1 -1
  68. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +1 -1
  69. package/common/cpp/audioapi/core/sources/StreamerNode.h +0 -8
  70. package/common/cpp/audioapi/core/{AudioParamEventQueue.cpp → utils/AudioParamEventQueue.cpp} +1 -1
  71. package/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h +45 -0
  72. package/common/cpp/audioapi/core/utils/worklets/UiWorkletsRunner.cpp +9 -0
  73. package/common/cpp/audioapi/core/utils/worklets/UiWorkletsRunner.h +73 -0
  74. package/common/cpp/audioapi/dsp/Windows.cpp +1 -1
  75. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +1 -1
  76. package/common/cpp/audioapi/jsi/AudioArrayBuffer.h +14 -1
  77. package/common/cpp/audioapi/jsi/JsiHostObject.h +6 -12
  78. package/common/cpp/audioapi/utils/AudioBus.cpp +1 -1
  79. package/common/cpp/test/CMakeLists.txt +8 -3
  80. package/common/cpp/test/GainTest.cpp +1 -1
  81. package/common/cpp/test/OscillatorTest.cpp +1 -1
  82. package/ios/audioapi/ios/AudioAPIModule.mm +32 -5
  83. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +1 -1
  84. package/ios/audioapi/ios/core/IOSAudioRecorder.h +1 -2
  85. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +1 -1
  86. package/lib/commonjs/api.js +7 -0
  87. package/lib/commonjs/api.js.map +1 -1
  88. package/lib/commonjs/core/BaseAudioContext.js +29 -0
  89. package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
  90. package/lib/commonjs/core/WorkletNode.js +11 -0
  91. package/lib/commonjs/core/WorkletNode.js.map +1 -0
  92. package/lib/commonjs/utils/index.js +9 -0
  93. package/lib/commonjs/utils/index.js.map +1 -1
  94. package/lib/module/api.js +1 -0
  95. package/lib/module/api.js.map +1 -1
  96. package/lib/module/core/BaseAudioContext.js +29 -0
  97. package/lib/module/core/BaseAudioContext.js.map +1 -1
  98. package/lib/module/core/WorkletNode.js +5 -0
  99. package/lib/module/core/WorkletNode.js.map +1 -0
  100. package/lib/module/utils/index.js +8 -0
  101. package/lib/module/utils/index.js.map +1 -1
  102. package/lib/typescript/api.d.ts +1 -0
  103. package/lib/typescript/api.d.ts.map +1 -1
  104. package/lib/typescript/core/BaseAudioContext.d.ts +2 -0
  105. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  106. package/lib/typescript/core/WorkletNode.d.ts +4 -0
  107. package/lib/typescript/core/WorkletNode.d.ts.map +1 -0
  108. package/lib/typescript/interfaces.d.ts +3 -0
  109. package/lib/typescript/interfaces.d.ts.map +1 -1
  110. package/lib/typescript/utils/index.d.ts +2 -0
  111. package/lib/typescript/utils/index.d.ts.map +1 -1
  112. package/package.json +3 -2
  113. package/src/api.ts +1 -0
  114. package/src/core/BaseAudioContext.ts +51 -0
  115. package/src/core/WorkletNode.ts +3 -0
  116. package/src/interfaces.ts +7 -0
  117. package/src/utils/index.ts +10 -0
  118. package/common/cpp/audioapi/HostObjects/AnalyserNodeHostObject.h +0 -149
  119. package/common/cpp/audioapi/HostObjects/AudioBufferBaseSourceNodeHostObject.h +0 -76
  120. package/common/cpp/audioapi/HostObjects/AudioBufferHostObject.h +0 -120
  121. package/common/cpp/audioapi/HostObjects/AudioBufferQueueSourceNodeHostObject.h +0 -67
  122. package/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h +0 -142
  123. package/common/cpp/audioapi/HostObjects/AudioRecorderHostObject.h +0 -86
  124. package/common/cpp/audioapi/HostObjects/AudioScheduledSourceNodeHostObject.h +0 -56
  125. package/common/cpp/audioapi/HostObjects/BiquadFilterNodeHostObject.h +0 -89
  126. package/common/cpp/audioapi/HostObjects/GainNodeHostObject.h +0 -27
  127. package/common/cpp/audioapi/HostObjects/OscillatorNodeHostObject.h +0 -65
  128. package/common/cpp/audioapi/HostObjects/StereoPannerNodeHostObject.h +0 -29
  129. package/common/cpp/audioapi/HostObjects/StreamerNodeHostObject.h +0 -30
  130. package/common/cpp/audioapi/events/AudioEventHandlerRegistryHostObject.h +0 -48
  131. package/ios/audioapi/ios/events/IOSAudioEventHandlerRegistry.h +0 -7
  132. package/ios/audioapi/ios/events/IOSAudioEventHandlerRegistry.mm +0 -12
  133. /package/common/cpp/audioapi/HostObjects/{AudioDestinationNodeHostObject.h → destinations/AudioDestinationNodeHostObject.h} +0 -0
  134. /package/common/cpp/audioapi/HostObjects/{PeriodicWaveHostObject.h → effects/PeriodicWaveHostObject.h} +0 -0
  135. /package/common/cpp/audioapi/core/{AudioParamEventQueue.h → utils/AudioParamEventQueue.h} +0 -0
  136. /package/common/cpp/audioapi/core/{Constants.h → utils/Constants.h} +0 -0
@@ -4,7 +4,7 @@ package_json = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
4
 
5
5
  $new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
6
6
 
7
- folly_flags = "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32"
7
+ folly_flags = "-DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32"
8
8
  fabric_flags = $new_arch_enabled ? '-DRCT_NEW_ARCH_ENABLED' : ''
9
9
  version_flag = "-DAUDIOAPI_VERSION=#{package_json['version']}"
10
10
 
@@ -42,18 +42,18 @@ Pod::Spec.new do |s|
42
42
  # Assumes Pods dir is nested under ios project dir
43
43
  ios_dir = File.join(Pod::Config.instance.project_pods_root, '..')
44
44
  rn_audio_dir_relative = Pathname.new(__dir__).relative_path_from(ios_dir).to_s
45
+
45
46
  external_dir_relative = "common/cpp/audioapi/external"
46
47
  lib_dir = "$(PROJECT_DIR)/#{rn_audio_dir_relative}/#{external_dir_relative}/$(PLATFORM_NAME)"
47
-
48
+
48
49
  external_dir = File.join(__dir__, "common/cpp/audioapi/external")
49
-
50
+
50
51
  s.ios.vendored_frameworks = [
51
52
  'common/cpp/audioapi/external/libavcodec.xcframework',
52
53
  'common/cpp/audioapi/external/libavformat.xcframework',
53
54
  'common/cpp/audioapi/external/libavutil.xcframework',
54
55
  'common/cpp/audioapi/external/libswresample.xcframework'
55
56
  ]
56
-
57
57
  s.pod_target_xcconfig = {
58
58
  "USE_HEADERMAP" => "YES",
59
59
  "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
@@ -61,12 +61,15 @@ s.pod_target_xcconfig = {
61
61
  "HEADER_SEARCH_PATHS" => %W[
62
62
  $(PODS_TARGET_SRCROOT)/common/cpp
63
63
  $(PODS_TARGET_SRCROOT)/ios
64
+ $(PODS_ROOT)/Headers/Public/RNWorklets
65
+ $(PODS_ROOT)/Headers/Private/React-Core
64
66
  $(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include
65
67
  $(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include/opus
66
68
  $(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include/vorbis
67
69
  $(PODS_TARGET_SRCROOT)/#{external_dir_relative}/ffmpeg_include
68
70
  ].join(" "),
69
- 'OTHER_CFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag}"
71
+ 'OTHER_CFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag}",
72
+ 'OTHER_CPLUSPLUSFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag}"
70
73
  }
71
74
 
72
75
  s.user_target_xcconfig = {
@@ -65,6 +65,15 @@ def resolveReactNativeDirectory() {
65
65
  )
66
66
  }
67
67
 
68
+ def resolveReactNativeWorkletsDirectory() {
69
+ def rnWorklets = rootProject.subprojects.find { it.name == 'react-native-worklets' }
70
+ if (rnWorklets != null) {
71
+ return rnWorklets.projectDir
72
+ }
73
+
74
+ return null;
75
+ }
76
+
68
77
  def toPlatformFileString(String path) {
69
78
  if (Os.isFamily(Os.FAMILY_WINDOWS)) {
70
79
  path = path.replace(File.separatorChar, '/' as char)
@@ -82,6 +91,8 @@ static def supportsNamespace() {
82
91
  }
83
92
 
84
93
  def reactNativeRootDir = resolveReactNativeDirectory()
94
+ def reactNativeWorkletsRootDir = resolveReactNativeWorkletsDirectory()
95
+ def isWorkletsAvailable = reactNativeWorkletsRootDir != null
85
96
 
86
97
  def reactProperties = new Properties()
87
98
  file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
@@ -112,17 +123,30 @@ android {
112
123
  defaultConfig {
113
124
  minSdkVersion getExtOrIntegerDefault("minSdkVersion")
114
125
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
126
+
115
127
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
128
+ buildConfigField "boolean", "RN_AUDIO_API_ENABLE_WORKLETS", "${isWorkletsAvailable}"
129
+
130
+
116
131
 
117
132
  externalNativeBuild {
118
133
  cmake {
119
134
  abiFilters (*reactNativeArchitectures())
120
- arguments "-DANDROID_STL=c++_shared",
121
- "-DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}",
122
- "-DANDROID_TOOLCHAIN=clang",
123
- "-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeRootDir.path)}",
135
+
136
+ def cmakeArgs = [
137
+ "-DANDROID_STL=c++_shared",
138
+ "-DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}",
139
+ "-DANDROID_TOOLCHAIN=clang",
140
+ "-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeRootDir.path)}",
141
+ "-DRN_AUDIO_API_WORKLETS_ENABLED=${isWorkletsAvailable}",
124
142
  "-DIS_NEW_ARCHITECTURE_ENABLED=${IS_NEW_ARCHITECTURE_ENABLED}",
125
143
  "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
144
+ ]
145
+
146
+ if (isWorkletsAvailable) {
147
+ cmakeArgs << "-DREACT_NATIVE_WORKLETS_DIR=${toPlatformFileString(reactNativeWorkletsRootDir.path)}"
148
+ }
149
+ arguments = cmakeArgs
126
150
  }
127
151
  }
128
152
  }
@@ -225,8 +249,24 @@ dependencies {
225
249
  implementation 'com.facebook.fbjni:fbjni:0.6.0'
226
250
  implementation 'com.google.oboe:oboe:1.9.3'
227
251
  implementation 'androidx.media:media:1.7.0'
252
+
253
+ if (isWorkletsAvailable) {
254
+ implementation(rootProject.subprojects.find { it.name == 'react-native-worklets' })
255
+ }
228
256
  }
229
257
 
258
+ if (isWorkletsAvailable) {
259
+ // Ensure worklets is built before react-native-audio-api
260
+ def rnWorkletsProject = rootProject.subprojects.find { it.name == 'react-native-worklets' }
261
+ evaluationDependsOn(rnWorkletsProject.path)
262
+
263
+ afterEvaluate {
264
+ tasks.getByName("buildCMakeDebug").dependsOn(rnWorkletsProject.tasks.getByName("mergeDebugNativeLibs"))
265
+ tasks.getByName("buildCMakeRelWithDebInfo").dependsOn(rnWorkletsProject.tasks.getByName("mergeReleaseNativeLibs"))
266
+ }
267
+ }
268
+
269
+
230
270
  def assertMinimalReactNativeVersion = task assertMinimalReactNativeVersionTask {
231
271
  // If you change the minimal React Native version remember to update Compatibility Table in docs
232
272
  def minimalReactNativeVersion = 76
@@ -27,6 +27,12 @@ find_package(ReactAndroid REQUIRED CONFIG)
27
27
  find_package(fbjni REQUIRED CONFIG)
28
28
  find_package(oboe REQUIRED CONFIG)
29
29
 
30
+ if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
31
+ set(BUILD_TYPE "debug")
32
+ else()
33
+ set(BUILD_TYPE "release")
34
+ endif()
35
+
30
36
  target_include_directories(
31
37
  react-native-audio-api
32
38
  PRIVATE
@@ -48,10 +54,69 @@ set(LINK_LIBRARIES
48
54
  oboe::oboe
49
55
  )
50
56
 
57
+ set(INCLUDE_LIBRARIES
58
+ "${COMMON_CPP_DIR}"
59
+ "${ANDROID_CPP_DIR}"
60
+ "${INCLUDE_DIR}"
61
+ "${INCLUDE_DIR}/opus"
62
+ "${INCLUDE_DIR}/vorbis"
63
+ "${REACT_NATIVE_DIR}/ReactCommon"
64
+ "${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor"
65
+ "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule"
66
+ "${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
67
+ )
68
+
69
+ if(RN_AUDIO_API_WORKLETS_ENABLED)
70
+ # Import the worklets library (similar to how Reanimated does it)
71
+ add_library(worklets SHARED IMPORTED)
72
+ set_target_properties(
73
+ worklets
74
+ PROPERTIES
75
+ IMPORTED_LOCATION
76
+ "${REACT_NATIVE_WORKLETS_DIR}/build/intermediates/cmake/${BUILD_TYPE}/obj/${ANDROID_ABI}/libworklets.so"
77
+ )
78
+ list(APPEND INCLUDE_LIBRARIES
79
+ "${REACT_NATIVE_WORKLETS_DIR}/../Common/cpp"
80
+ "${REACT_NATIVE_WORKLETS_DIR}/src/main/cpp"
81
+ )
82
+ list(APPEND LINK_LIBRARIES worklets)
83
+ endif()
84
+
85
+ target_include_directories(
86
+ react-native-audio-api
87
+ PRIVATE
88
+ ${INCLUDE_LIBRARIES}
89
+ )
90
+
91
+ if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
92
+ set(RN_VERSION_LINK_LIBRARIES
93
+ ReactAndroid::reactnative
94
+ )
95
+ else()
96
+ set(RN_VERSION_LINK_LIBRARIES
97
+ ReactAndroid::folly_runtime
98
+ ReactAndroid::react_nativemodule_core
99
+ ReactAndroid::glog
100
+ ReactAndroid::reactnativejni
101
+ )
102
+ endif()
103
+
51
104
  set(RN_VERSION_LINK_LIBRARIES
52
105
  ReactAndroid::reactnative
53
106
  )
54
107
 
108
+ if(RN_AUDIO_API_WORKLETS_ENABLED)
109
+ target_compile_definitions(
110
+ react-native-audio-api
111
+ PRIVATE RN_AUDIO_API_ENABLE_WORKLETS=1
112
+ )
113
+ else()
114
+ target_compile_definitions(
115
+ react-native-audio-api
116
+ PRIVATE RN_AUDIO_API_ENABLE_WORKLETS=0
117
+ )
118
+ endif()
119
+
55
120
  target_link_libraries(react-native-audio-api
56
121
  ${LINK_LIBRARIES}
57
122
  ${RN_VERSION_LINK_LIBRARIES}
@@ -6,10 +6,16 @@ using namespace facebook::jni;
6
6
 
7
7
  AudioAPIModule::AudioAPIModule(
8
8
  jni::alias_ref<AudioAPIModule::jhybridobject> &jThis,
9
+ #if RN_AUDIO_API_ENABLE_WORKLETS
10
+ std::weak_ptr<WorkletsModuleProxy> weakWorkletsModuleProxy,
11
+ #endif
9
12
  jsi::Runtime *jsiRuntime,
10
13
  const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker)
11
14
  : javaPart_(make_global(jThis)),
12
15
  jsiRuntime_(jsiRuntime),
16
+ #if RN_AUDIO_API_ENABLE_WORKLETS
17
+ weakWorkletsModuleProxy_(weakWorkletsModuleProxy),
18
+ #endif
13
19
  jsCallInvoker_(jsCallInvoker) {
14
20
  audioEventHandlerRegistry_ =
15
21
  std::make_shared<AudioEventHandlerRegistry>(jsiRuntime, jsCallInvoker);
@@ -17,12 +23,25 @@ AudioAPIModule::AudioAPIModule(
17
23
 
18
24
  jni::local_ref<AudioAPIModule::jhybriddata> AudioAPIModule::initHybrid(
19
25
  jni::alias_ref<jhybridobject> jThis,
26
+ jni::alias_ref<jni::JObject> jWorkletsModule,
20
27
  jlong jsContext,
21
28
  jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>
22
29
  jsCallInvokerHolder) {
23
30
  auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker();
24
31
  auto rnRuntime = reinterpret_cast<jsi::Runtime *>(jsContext);
32
+ #if RN_AUDIO_API_ENABLE_WORKLETS
33
+ if (jWorkletsModule) {
34
+ auto castedModule =
35
+ jni::static_ref_cast<WorkletsModule::javaobject>(jWorkletsModule);
36
+ auto workletsModuleProxy = castedModule->cthis()->getWorkletsModuleProxy();
37
+ return makeCxxInstance(
38
+ jThis, workletsModuleProxy, rnRuntime, jsCallInvoker);
39
+ }
40
+ throw std::runtime_error(
41
+ "Worklets module is required but not provided from Java/Kotlin side");
42
+ #else
25
43
  return makeCxxInstance(jThis, rnRuntime, jsCallInvoker);
44
+ #endif
26
45
  }
27
46
 
28
47
  void AudioAPIModule::registerNatives() {
@@ -36,8 +55,17 @@ void AudioAPIModule::registerNatives() {
36
55
  }
37
56
 
38
57
  void AudioAPIModule::injectJSIBindings() {
58
+ #if RN_AUDIO_API_ENABLE_WORKLETS
59
+ auto uiWorkletRuntime =
60
+ weakWorkletsModuleProxy_.lock()->getUIWorkletRuntime();
61
+ #else
62
+ auto uiWorkletRuntime = nullptr;
63
+ #endif
39
64
  AudioAPIModuleInstaller::injectJSIBindings(
40
- jsiRuntime_, jsCallInvoker_, audioEventHandlerRegistry_);
65
+ jsiRuntime_,
66
+ jsCallInvoker_,
67
+ audioEventHandlerRegistry_,
68
+ uiWorkletRuntime);
41
69
  }
42
70
 
43
71
  void AudioAPIModule::invokeHandlerWithEventNameAndEventBody(
@@ -7,6 +7,7 @@
7
7
  #include <fbjni/fbjni.h>
8
8
  #include <react/jni/CxxModuleWrapper.h>
9
9
  #include <react/jni/JMessageQueueThread.h>
10
+ #include <audioapi/core/utils/worklets/SafeIncludes.h>
10
11
  #include <memory>
11
12
  #include <utility>
12
13
  #include <unordered_map>
@@ -15,6 +16,7 @@ namespace audioapi {
15
16
 
16
17
  using namespace facebook;
17
18
  using namespace react;
19
+ using namespace worklets;
18
20
 
19
21
  class AudioAPIModule : public jni::HybridClass<AudioAPIModule> {
20
22
  public:
@@ -23,6 +25,7 @@ class AudioAPIModule : public jni::HybridClass<AudioAPIModule> {
23
25
 
24
26
  static jni::local_ref<AudioAPIModule::jhybriddata> initHybrid(
25
27
  jni::alias_ref<jhybridobject> jThis,
28
+ jni::alias_ref<jni::JObject> jWorkletsModule, // it will be null if RN_AUDIO_API_ENABLE_WORKLETS is false
26
29
  jlong jsContext,
27
30
  jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>
28
31
  jsCallInvokerHolder);
@@ -37,13 +40,24 @@ class AudioAPIModule : public jni::HybridClass<AudioAPIModule> {
37
40
 
38
41
  jni::global_ref<AudioAPIModule::javaobject> javaPart_;
39
42
  jsi::Runtime *jsiRuntime_;
43
+ #if RN_AUDIO_API_ENABLE_WORKLETS
44
+ std::weak_ptr<worklets::WorkletsModuleProxy> weakWorkletsModuleProxy_;
45
+ #endif
40
46
  std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker_;
41
47
  std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
42
48
 
49
+ #if RN_AUDIO_API_ENABLE_WORKLETS
43
50
  explicit AudioAPIModule(
44
51
  jni::alias_ref<AudioAPIModule::jhybridobject> &jThis,
52
+ std::weak_ptr<worklets::WorkletsModuleProxy> weakWorkletsModuleProxy,
45
53
  jsi::Runtime *jsiRuntime,
46
54
  const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker);
55
+ #else
56
+ explicit AudioAPIModule(
57
+ jni::alias_ref<AudioAPIModule::jhybridobject> &jThis,
58
+ jsi::Runtime *jsiRuntime,
59
+ const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker);
60
+ #endif
47
61
  };
48
62
 
49
63
  } // namespace audioapi
@@ -1,6 +1,6 @@
1
1
  #include <audioapi/android/core/AndroidAudioRecorder.h>
2
- #include <audioapi/core/Constants.h>
3
2
  #include <audioapi/core/sources/RecorderAdapterNode.h>
3
+ #include <audioapi/core/utils/Constants.h>
4
4
  #include <audioapi/events/AudioEventHandlerRegistry.h>
5
5
  #include <audioapi/utils/AudioArray.h>
6
6
  #include <audioapi/utils/AudioBus.h>
@@ -25,9 +25,13 @@ AndroidAudioRecorder::AndroidAudioRecorder(
25
25
  ->setDataCallback(this)
26
26
  ->setSampleRate(static_cast<int>(sampleRate))
27
27
  ->openStream(mStream_);
28
+
29
+ nativeAudioRecorder_ = jni::make_global(NativeAudioRecorder::create());
28
30
  }
29
31
 
30
32
  AndroidAudioRecorder::~AndroidAudioRecorder() {
33
+ nativeAudioRecorder_.release();
34
+
31
35
  if (mStream_) {
32
36
  mStream_->requestStop();
33
37
  mStream_->close();
@@ -41,6 +45,7 @@ void AndroidAudioRecorder::start() {
41
45
  }
42
46
 
43
47
  if (mStream_) {
48
+ nativeAudioRecorder_->start();
44
49
  mStream_->requestStart();
45
50
  }
46
51
 
@@ -55,6 +60,7 @@ void AndroidAudioRecorder::stop() {
55
60
  isRunning_.store(false);
56
61
 
57
62
  if (mStream_) {
63
+ nativeAudioRecorder_->stop();
58
64
  mStream_->requestStop();
59
65
  }
60
66
 
@@ -6,6 +6,8 @@
6
6
  #include <functional>
7
7
  #include <memory>
8
8
 
9
+ #include <audioapi/android/core/NativeAudioRecorder.hpp>
10
+
9
11
  namespace audioapi {
10
12
 
11
13
  using namespace oboe;
@@ -16,7 +18,8 @@ class AndroidAudioRecorder : public AudioStreamDataCallback, public AudioRecorde
16
18
  public:
17
19
  AndroidAudioRecorder(float sampleRate,
18
20
  int bufferLength,
19
- const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);
21
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry
22
+ );
20
23
 
21
24
  ~AndroidAudioRecorder() override;
22
25
 
@@ -30,6 +33,8 @@ class AndroidAudioRecorder : public AudioStreamDataCallback, public AudioRecorde
30
33
 
31
34
  private:
32
35
  std::shared_ptr<AudioStream> mStream_;
36
+
37
+ facebook::jni::global_ref<NativeAudioRecorder> nativeAudioRecorder_;
33
38
  };
34
39
 
35
40
  } // namespace audioapi
@@ -1,7 +1,7 @@
1
1
  #include <android/log.h>
2
2
  #include <audioapi/android/core/AudioPlayer.h>
3
3
  #include <audioapi/core/AudioContext.h>
4
- #include <audioapi/core/Constants.h>
4
+ #include <audioapi/core/utils/Constants.h>
5
5
  #include <audioapi/utils/AudioArray.h>
6
6
  #include <audioapi/utils/AudioBus.h>
7
7
 
@@ -0,0 +1,36 @@
1
+ #pragma once
2
+
3
+
4
+ #include <fbjni/fbjni.h>
5
+ #include <react/jni/CxxModuleWrapper.h>
6
+ #include <react/jni/JMessageQueueThread.h>
7
+ #include <memory>
8
+ #include <utility>
9
+ #include <unordered_map>
10
+
11
+ namespace audioapi {
12
+
13
+ using namespace facebook;
14
+ using namespace react;
15
+
16
+ class NativeAudioRecorder : public jni::JavaClass<NativeAudioRecorder> {
17
+ public:
18
+ static auto constexpr kJavaDescriptor =
19
+ "Lcom/swmansion/audioapi/core/NativeAudioRecorder;";
20
+
21
+ static jni::local_ref<NativeAudioRecorder> create() {
22
+ return newInstance();
23
+ }
24
+
25
+ void start() {
26
+ static const auto method = javaClassStatic()->getMethod<void()>("start");
27
+ method(self());
28
+ }
29
+
30
+ void stop() {
31
+ static const auto method = javaClassStatic()->getMethod<void()>("stop");
32
+ method(self());
33
+ }
34
+ };
35
+
36
+ } // namespace audioapi
@@ -26,6 +26,7 @@ class AudioAPIModule(
26
26
  private val mHybridData: HybridData
27
27
 
28
28
  external fun initHybrid(
29
+ workletsModule: Any?,
29
30
  jsContext: Long,
30
31
  callInvoker: CallInvokerHolderImpl,
31
32
  ): HybridData
@@ -41,7 +42,16 @@ class AudioAPIModule(
41
42
  try {
42
43
  System.loadLibrary("react-native-audio-api")
43
44
  val jsCallInvokerHolder = reactContext.jsCallInvokerHolder as CallInvokerHolderImpl
44
- mHybridData = initHybrid(reactContext.javaScriptContextHolder!!.get(), jsCallInvokerHolder)
45
+
46
+ var workletsModule: Any? = null
47
+ if (BuildConfig.RN_AUDIO_API_ENABLE_WORKLETS) {
48
+ try {
49
+ workletsModule = reactContext.getNativeModule("WorkletsModule")
50
+ } catch (ex: Exception) {
51
+ throw RuntimeException("WorkletsModule not found - make sure react-native-worklets is properly installed")
52
+ }
53
+ }
54
+ mHybridData = initHybrid(workletsModule, reactContext.javaScriptContextHolder!!.get(), jsCallInvokerHolder)
45
55
  } catch (exception: UnsatisfiedLinkError) {
46
56
  throw RuntimeException("Could not load native module AudioAPIModule", exception)
47
57
  }
@@ -0,0 +1,24 @@
1
+ package com.swmansion.audioapi.core
2
+
3
+ import com.facebook.common.internal.DoNotStrip
4
+ import com.swmansion.audioapi.system.MediaSessionManager
5
+
6
+ @DoNotStrip
7
+ class NativeAudioRecorder {
8
+ private var inputNodeId: String? = null
9
+
10
+ @DoNotStrip
11
+ fun start() {
12
+ this.inputNodeId = MediaSessionManager.attachAudioRecorder(this)
13
+ MediaSessionManager.startForegroundServiceIfNecessary()
14
+ }
15
+
16
+ @DoNotStrip
17
+ fun stop() {
18
+ this.inputNodeId?.let {
19
+ MediaSessionManager.detachAudioRecorder(it)
20
+ this.inputNodeId = null
21
+ }
22
+ MediaSessionManager.stopForegroundServiceIfNecessary()
23
+ }
24
+ }
@@ -21,6 +21,7 @@ import com.facebook.react.modules.core.PermissionAwareActivity
21
21
  import com.facebook.react.modules.core.PermissionListener
22
22
  import com.swmansion.audioapi.AudioAPIModule
23
23
  import com.swmansion.audioapi.core.NativeAudioPlayer
24
+ import com.swmansion.audioapi.core.NativeAudioRecorder
24
25
  import com.swmansion.audioapi.system.PermissionRequestListener.Companion.RECORDING_REQUEST_CODE
25
26
  import java.lang.ref.WeakReference
26
27
  import java.util.UUID
@@ -42,6 +43,7 @@ object MediaSessionManager {
42
43
  private var isServiceRunning = false
43
44
  private val serviceStateLock = Any()
44
45
  private val nativeAudioPlayers = mutableMapOf<String, NativeAudioPlayer>()
46
+ private val nativeAudioRecorders = mutableMapOf<String, NativeAudioRecorder>()
45
47
 
46
48
  fun initialize(
47
49
  audioAPIModule: WeakReference<AudioAPIModule>,
@@ -95,14 +97,25 @@ object MediaSessionManager {
95
97
  nativeAudioPlayers.remove(uuid)
96
98
  }
97
99
 
100
+ fun attachAudioRecorder(recorder: NativeAudioRecorder): String {
101
+ val uuid = UUID.randomUUID().toString()
102
+ nativeAudioRecorders[uuid] = recorder
103
+
104
+ return uuid
105
+ }
106
+
107
+ fun detachAudioRecorder(uuid: String) {
108
+ nativeAudioRecorders.remove(uuid)
109
+ }
110
+
98
111
  fun startForegroundServiceIfNecessary() {
99
- if (nativeAudioPlayers.isNotEmpty()) {
112
+ if (nativeAudioPlayers.isNotEmpty() || nativeAudioRecorders.isNotEmpty()) {
100
113
  startForegroundService()
101
114
  }
102
115
  }
103
116
 
104
117
  fun stopForegroundServiceIfNecessary() {
105
- if (nativeAudioPlayers.isEmpty()) {
118
+ if (nativeAudioPlayers.isEmpty() && nativeAudioRecorders.isEmpty()) {
106
119
  stopForegroundService()
107
120
  }
108
121
  }
@@ -6,10 +6,12 @@
6
6
  #include <audioapi/core/inputs/AudioRecorder.h>
7
7
  #include <audioapi/HostObjects/AudioContextHostObject.h>
8
8
  #include <audioapi/HostObjects/OfflineAudioContextHostObject.h>
9
- #include <audioapi/HostObjects/AudioRecorderHostObject.h>
9
+ #include <audioapi/HostObjects/inputs/AudioRecorderHostObject.h>
10
10
 
11
11
  #include <audioapi/events/AudioEventHandlerRegistry.h>
12
- #include <audioapi/events/AudioEventHandlerRegistryHostObject.h>
12
+ #include <audioapi/HostObjects/events/AudioEventHandlerRegistryHostObject.h>
13
+
14
+ #include <audioapi/core/utils/worklets/SafeIncludes.h>
13
15
 
14
16
  #include <memory>
15
17
 
@@ -19,10 +21,16 @@ using namespace facebook;
19
21
 
20
22
  class AudioAPIModuleInstaller {
21
23
  public:
22
- static void injectJSIBindings(jsi::Runtime *jsiRuntime, const std::shared_ptr<react::CallInvoker> &jsCallInvoker, const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry) {
23
- auto createAudioContext = getCreateAudioContextFunction(jsiRuntime, jsCallInvoker, audioEventHandlerRegistry);
24
+ static void injectJSIBindings(
25
+ jsi::Runtime *jsiRuntime,
26
+ const std::shared_ptr<react::CallInvoker> &jsCallInvoker,
27
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
28
+ std::shared_ptr<worklets::WorkletRuntime> uiRuntime = nullptr) {
29
+ auto workletRunner = std::make_shared<UiWorkletsRunner>(uiRuntime);
30
+
31
+ auto createAudioContext = getCreateAudioContextFunction(jsiRuntime, jsCallInvoker, audioEventHandlerRegistry, workletRunner);
24
32
  auto createAudioRecorder = getCreateAudioRecorderFunction(jsiRuntime, audioEventHandlerRegistry);
25
- auto createOfflineAudioContext = getCreateOfflineAudioContextFunction(jsiRuntime, jsCallInvoker, audioEventHandlerRegistry);
33
+ auto createOfflineAudioContext = getCreateOfflineAudioContextFunction(jsiRuntime, jsCallInvoker, audioEventHandlerRegistry, workletRunner);
26
34
 
27
35
  jsiRuntime->global().setProperty(*jsiRuntime, "createAudioContext", createAudioContext);
28
36
  jsiRuntime->global().setProperty(*jsiRuntime, "createAudioRecorder", createAudioRecorder);
@@ -33,12 +41,16 @@ class AudioAPIModuleInstaller {
33
41
  }
34
42
 
35
43
  private:
36
- static jsi::Function getCreateAudioContextFunction(jsi::Runtime *jsiRuntime, const std::shared_ptr<react::CallInvoker> &jsCallInvoker, const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry) {
44
+ static jsi::Function getCreateAudioContextFunction(
45
+ jsi::Runtime *jsiRuntime,
46
+ const std::shared_ptr<react::CallInvoker> &jsCallInvoker,
47
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
48
+ const std::shared_ptr<UiWorkletsRunner> &workletRunner) {
37
49
  return jsi::Function::createFromHostFunction(
38
50
  *jsiRuntime,
39
51
  jsi::PropNameID::forAscii(*jsiRuntime, "createAudioContext"),
40
52
  0,
41
- [jsCallInvoker, audioEventHandlerRegistry](
53
+ [jsCallInvoker, audioEventHandlerRegistry, workletRunner](
42
54
  jsi::Runtime &runtime,
43
55
  const jsi::Value &thisValue,
44
56
  const jsi::Value *args,
@@ -46,7 +58,7 @@ class AudioAPIModuleInstaller {
46
58
  std::shared_ptr<AudioContext> audioContext;
47
59
  auto sampleRate = static_cast<float>(args[0].getNumber());
48
60
  auto initSuspended = args[1].getBool();
49
- audioContext = std::make_shared<AudioContext>(sampleRate, initSuspended, audioEventHandlerRegistry);
61
+ audioContext = std::make_shared<AudioContext>(sampleRate, initSuspended, audioEventHandlerRegistry, workletRunner);
50
62
 
51
63
  auto audioContextHostObject = std::make_shared<AudioContextHostObject>(
52
64
  audioContext, &runtime, jsCallInvoker);
@@ -56,12 +68,16 @@ class AudioAPIModuleInstaller {
56
68
  });
57
69
  }
58
70
 
59
- static jsi::Function getCreateOfflineAudioContextFunction(jsi::Runtime *jsiRuntime, const std::shared_ptr<react::CallInvoker> &jsCallInvoker, const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry) {
71
+ static jsi::Function getCreateOfflineAudioContextFunction(
72
+ jsi::Runtime *jsiRuntime,
73
+ const std::shared_ptr<react::CallInvoker> &jsCallInvoker,
74
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
75
+ const std::shared_ptr<UiWorkletsRunner> &workletRunner) {
60
76
  return jsi::Function::createFromHostFunction(
61
77
  *jsiRuntime,
62
78
  jsi::PropNameID::forAscii(*jsiRuntime, "createOfflineAudioContext"),
63
79
  0,
64
- [jsCallInvoker, audioEventHandlerRegistry](
80
+ [jsCallInvoker, audioEventHandlerRegistry, workletRunner](
65
81
  jsi::Runtime &runtime,
66
82
  const jsi::Value &thisValue,
67
83
  const jsi::Value *args,
@@ -70,7 +86,7 @@ class AudioAPIModuleInstaller {
70
86
  auto length = static_cast<size_t>(args[1].getNumber());
71
87
  auto sampleRate = static_cast<float>(args[2].getNumber());
72
88
 
73
- auto offlineAudioContext = std::make_shared<OfflineAudioContext>(numberOfChannels, length, sampleRate, audioEventHandlerRegistry);
89
+ auto offlineAudioContext = std::make_shared<OfflineAudioContext>(numberOfChannels, length, sampleRate, audioEventHandlerRegistry, workletRunner);
74
90
  auto audioContextHostObject = std::make_shared<OfflineAudioContextHostObject>(
75
91
  offlineAudioContext, &runtime, jsCallInvoker);
76
92
 
@@ -79,7 +95,9 @@ class AudioAPIModuleInstaller {
79
95
  });
80
96
  }
81
97
 
82
- static jsi::Function getCreateAudioRecorderFunction(jsi::Runtime *jsiRuntime, const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry) {
98
+ static jsi::Function getCreateAudioRecorderFunction(
99
+ jsi::Runtime *jsiRuntime,
100
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry) {
83
101
  return jsi::Function::createFromHostFunction(
84
102
  *jsiRuntime,
85
103
  jsi::PropNameID::forAscii(*jsiRuntime, "createAudioRecorder"),
@@ -94,7 +112,7 @@ class AudioAPIModuleInstaller {
94
112
  auto sampleRate = static_cast<float>(options.getProperty(runtime, "sampleRate").getNumber());
95
113
  auto bufferLength = static_cast<int>(options.getProperty(runtime, "bufferLengthInSamples").getNumber());
96
114
 
97
- auto audioRecorderHostObject = std::make_shared<AudioRecorderHostObject>(&runtime, audioEventHandlerRegistry, sampleRate, bufferLength);
115
+ auto audioRecorderHostObject = std::make_shared<AudioRecorderHostObject>(audioEventHandlerRegistry, sampleRate, bufferLength);
98
116
 
99
117
  return jsi::Object::createFromHostObject(runtime, audioRecorderHostObject);
100
118
  });