From 023a14594017fa6919388b2aae6dce755324fdf8 Mon Sep 17 00:00:00 2001 From: maciejmakowski2003 Date: Tue, 7 Jul 2026 12:11:47 +0200 Subject: [PATCH 1/8] refactor: removed worklets integration in main package and exposed stable api --- .../build-compilation-dependencies/SKILL.md | 6 +- .../build-details.md | 27 +++- .../react-native-audio-api/RNAudioAPI.podspec | 21 +-- .../android/build.gradle | 105 ++++++------ .../android/fix-prefab.gradle | 61 +++++++ .../src/main/cpp/audioapi/CMakeLists.txt | 78 ++------- .../cpp/audioapi/android/AudioAPIModule.cpp | 27 +--- .../cpp/audioapi/android/AudioAPIModule.h | 15 -- .../com/swmansion/audioapi/AudioAPIModule.kt | 12 +- .../cpp/audioapi/AudioAPIModuleInstaller.h | 44 ++--- .../HostObjects/AudioContextHostObject.cpp | 3 +- .../HostObjects/AudioContextHostObject.h | 2 - .../BaseAudioContextHostObject.cpp | 68 -------- .../HostObjects/BaseAudioContextHostObject.h | 9 +- .../OfflineAudioContextHostObject.cpp | 4 +- .../OfflineAudioContextHostObject.h | 2 - .../effects/WorkletNodeHostObject.h | 30 ---- .../effects/WorkletProcessingNodeHostObject.h | 32 ---- .../sources/WorkletSourceNodeHostObject.h | 29 ---- .../audioapi/compatibility/EXTENSION_API.md | 152 ++++++++++++++++++ .../cpp/audioapi/compatibility/StableAPI.h | 17 ++ .../common/cpp/audioapi/core/AudioContext.cpp | 6 +- .../common/cpp/audioapi/core/AudioContext.h | 4 +- .../cpp/audioapi/core/BaseAudioContext.cpp | 9 +- .../cpp/audioapi/core/BaseAudioContext.h | 6 +- .../cpp/audioapi/core/OfflineAudioContext.cpp | 5 +- .../cpp/audioapi/core/OfflineAudioContext.h | 4 +- .../cpp/audioapi/core/effects/WorkletNode.cpp | 67 -------- .../cpp/audioapi/core/effects/WorkletNode.h | 58 ------- .../core/effects/WorkletProcessingNode.cpp | 78 --------- .../core/effects/WorkletProcessingNode.h | 48 ------ .../core/sources/WorkletSourceNode.cpp | 84 ---------- .../audioapi/core/sources/WorkletSourceNode.h | 44 ----- .../core/utils/worklets/SafeIncludes.h | 88 ---------- .../core/utils/worklets/WorkletsRunner.cpp | 80 --------- .../core/utils/worklets/WorkletsRunner.h | 90 ----------- .../common/cpp/clangd/CMakeLists.txt | 13 -- .../common/cpp/test/CMakeLists.txt | 3 - .../cpp/test/src/core/AudioParamTest.cpp | 4 +- .../cpp/test/src/core/effects/DelayTest.cpp | 4 +- .../cpp/test/src/core/effects/GainTest.cpp | 4 +- .../test/src/core/effects/IIRFilterTest.cpp | 4 +- .../src/core/effects/StereoPannerTest.cpp | 4 +- .../src/core/effects/TailProcessingTest.cpp | 4 +- .../src/core/effects/WaveShaperNodeTest.cpp | 4 +- .../core/effects/biquad/BiquadFilterTest.h | 4 +- .../sources/AudioBufferQueueSourceTest.cpp | 8 +- .../core/sources/AudioScheduledSourceTest.cpp | 4 +- .../src/core/sources/ConstantSourceTest.cpp | 4 +- .../MediaElementAudioSourceNodeTest.cpp | 3 +- .../test/src/core/sources/OscillatorTest.cpp | 4 +- .../cpp/test/src/graph/TestGraphUtils.h | 3 +- .../ios/audioapi/ios/AudioAPIModule.mm | 38 ----- packages/react-native-audio-api/package.json | 9 +- .../scripts/rnaa_utils.rb | 22 +-- .../scripts/validate-worklets-version.js | 29 ---- .../src/Audio/controls/AudioControls.tsx | 18 ++- .../src/AudioAPIModule/AudioAPIModule.ts | 89 +--------- .../src/AudioAPIModule/AudioAPIModule.web.ts | 17 +- .../src/AudioAPIModule/ModuleInterfaces.ts | 25 --- .../src/AudioAPIModule/globals.d.ts | 10 +- packages/react-native-audio-api/src/api.ts | 3 - .../src/core/AudioContext.ts | 6 +- .../src/core/BaseAudioContext.ts | 60 +------ .../src/core/OfflineAudioContext.ts | 10 +- .../src/core/WorkletNode.ts | 32 ---- .../src/core/WorkletProcessingNode.ts | 41 ----- .../src/core/WorkletSourceNode.ts | 38 ----- .../src/jsi-interfaces.ts | 44 ----- .../react-native-audio-api/src/mock/index.ts | 76 --------- packages/react-native-audio-api/src/types.ts | 2 - .../react-native-audio-api/src/utils/index.ts | 18 --- .../tests/integration.test.ts | 50 ------ .../react-native-audio-api/tests/mock.test.ts | 30 ---- packages/test-app-screen/src/Test.tsx | 29 +--- packages/test-app-screen/src/WorkletsTest.ts | 31 ---- 76 files changed, 389 insertions(+), 1827 deletions(-) create mode 100644 packages/react-native-audio-api/android/fix-prefab.gradle delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WorkletNodeHostObject.h delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WorkletProcessingNodeHostObject.h delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/WorkletSourceNodeHostObject.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/compatibility/EXTENSION_API.md create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/compatibility/StableAPI.h delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.cpp delete mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.h delete mode 100644 packages/react-native-audio-api/scripts/validate-worklets-version.js delete mode 100644 packages/react-native-audio-api/src/AudioAPIModule/ModuleInterfaces.ts delete mode 100644 packages/react-native-audio-api/src/core/WorkletNode.ts delete mode 100644 packages/react-native-audio-api/src/core/WorkletProcessingNode.ts delete mode 100644 packages/react-native-audio-api/src/core/WorkletSourceNode.ts delete mode 100644 packages/test-app-screen/src/WorkletsTest.ts diff --git a/.claude/skills/build-compilation-dependencies/SKILL.md b/.claude/skills/build-compilation-dependencies/SKILL.md index b7fe5f518..d8868f273 100644 --- a/.claude/skills/build-compilation-dependencies/SKILL.md +++ b/.claude/skills/build-compilation-dependencies/SKILL.md @@ -84,15 +84,19 @@ Downloaded artifacts land in: ### Files - `android/build.gradle` — Gradle library config +- `android/fix-prefab.gradle` — prefab publication workaround (ported from react-native-worklets) - `android/CMakeLists.txt` — Android CMake root (SIMD detection, RN version flags, delegates to subdirectory) - `android/src/main/cpp/audioapi/CMakeLists.txt` — actual build target (sources, prebuilt libs, include paths) +- `common/cpp/audioapi/EXTENSION_API.md` — stable C++ extension contract for dependent native modules +- `common/cpp/audioapi/compatibility/StableAPI.h` — single public C++ compatibility header for extensions ### Key behaviors - Feature flags (`newArchEnabled`, `disableAudioapiFFmpeg`) are read from app's `gradle.properties` and forwarded to both CMake and Kotlin `BuildConfig` - DSP sources always compiled with `-O3` regardless of overall build type - Sources gathered with `GLOB_RECURSE CONFIGURE_DEPENDS` — CMake re-runs automatically when files are added/removed -- Worklets must be merged before the audio API CMake build starts (explicit Gradle task dependency) - 16KB page size alignment enabled for Android 15+ +- **Extension API (prefab)**: single public C++ header ``; prefab publishes transitive headers needed to compile it (`prepareAudioApiHeadersForPrefabs`); `fix-prefab.gradle` ensures the `.so` is in prefab metadata. Contract: `EXTENSION_API.md` +- CMake exposes `COMMON_CPP_DIR` and `ANDROID_CPP_DIR` as **PUBLIC** include dirs so prefab consumers resolve `` For full per-line analysis see [build-details.md](build-details.md#android-androidcmakeliststxt-root--detailed-analysis). diff --git a/.claude/skills/build-compilation-dependencies/build-details.md b/.claude/skills/build-compilation-dependencies/build-details.md index 970e99d92..ad6440149 100644 --- a/.claude/skills/build-compilation-dependencies/build-details.md +++ b/.claude/skills/build-compilation-dependencies/build-details.md @@ -42,12 +42,31 @@ packagingOptions { } ``` -### Worklets dependency ordering - -Worklets must be merged before the audio API's CMake build starts. Gradle task dependency is wired explicitly: +### Prefab extension API ```groovy -tasks.getByName("buildCMakeDebug").dependsOn(rnWorkletsProject.tasks.getByName("mergeDebugNativeLibs")) +buildFeatures { + prefab true + prefabPublishing true +} + +prefab { + "react-native-audio-api" { + headers audioApiPrefabHeadersDir.absolutePath + } +} +``` + +`prepareAudioApiHeadersForPrefabs` copies `audioapi/**/*.{h,hpp}` (include-only, no +excludes — same approach as react-native-worklets). `fix-prefab.gradle` makes +`prefab*ConfigurePackage` depend on `externalNativeBuild*` and touches dependents' +`prefab_config.json`. + +Consumers: + +```cmake +find_package(react-native-audio-api REQUIRED CONFIG) +target_link_libraries(my-ext react-native-audio-api::react-native-audio-api) ``` ### Minimum RN version enforcement diff --git a/packages/react-native-audio-api/RNAudioAPI.podspec b/packages/react-native-audio-api/RNAudioAPI.podspec index 691a2d0cd..ebf5e2ea4 100644 --- a/packages/react-native-audio-api/RNAudioAPI.podspec +++ b/packages/react-native-audio-api/RNAudioAPI.podspec @@ -12,9 +12,6 @@ fabric_flags = $new_arch_enabled ? '-DRCT_NEW_ARCH_ENABLED' : '' version_flag = "-DAUDIOAPI_VERSION=#{package_json['version']}" ios_min_version = '14.0' -worklets_enabled = $audio_api_config[:worklets_enabled] -worklets_preprocessor_flag = worklets_enabled ? '-DRN_AUDIO_API_ENABLE_WORKLETS=1' : '' - ffmpeg_flag = $RN_AUDIO_API_FFMPEG_DISABLED ? '-DRN_AUDIO_API_FFMPEG_DISABLED=1' : '' static_external_libs_flag = $RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED ? '-DRN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED=1 -DMA_NO_LIBOPUS=1 -DMA_NO_LIBVORBIS=1' : '' skip_ffmpeg_argument = $RN_AUDIO_API_FFMPEG_DISABLED ? 'skipffmpeg' : '' @@ -59,10 +56,6 @@ Pod::Spec.new do |s| end end - if worklets_enabled - s.dependency 'RNWorklets' - end - s.ios.frameworks = 'Accelerate', 'AVFoundation', 'MediaPlayer' s.prepare_command = <<-CMD @@ -141,16 +134,11 @@ Pod::Spec.new do |s| "\"$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include/vorbis\"" ]) .concat($RN_AUDIO_API_FFMPEG_DISABLED ? [] : ["\"$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include_ffmpeg\""]) - .concat(worklets_enabled ? [ - '"$(PODS_ROOT)/Headers/Public/RNWorklets"', - '"$(PODS_ROOT)/Headers/Private/ReactCodegen"', - '"$(PODS_ROOT)/../build/generated/ios/ReactCodegen"', - ] : []) .join(' '), "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) HAVE_ACCELERATE=1', "GCC_PREPROCESSOR_DEFINITIONS[config=Debug]" => '$(inherited) HAVE_ACCELERATE=1 DEBUG=1', - 'OTHER_CFLAGS' => "$(inherited) #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag} #{ffmpeg_flag} #{static_external_libs_flag}", + 'OTHER_CFLAGS' => "$(inherited) #{fabric_flags} #{version_flag} #{ffmpeg_flag} #{static_external_libs_flag}", } s.xcconfig = { @@ -165,13 +153,6 @@ Pod::Spec.new do |s| "\"$(PODS_ROOT)/#{$audio_api_config[:dynamic_frameworks_audio_api_dir]}/ios\"", "\"$(PODS_ROOT)/#{$audio_api_config[:dynamic_frameworks_audio_api_dir]}/common/cpp\"", ] - .concat(worklets_enabled ? [ - '"$(PODS_ROOT)/Headers/Public/RNWorklets"', - '"$(PODS_ROOT)/Headers/Private/ReactCodegen"', - '"$(PODS_ROOT)/../build/generated/ios/ReactCodegen"', - "\"$(PODS_ROOT)/#{$audio_api_config[:dynamic_frameworks_worklets_dir]}/apple\"", - "\"$(PODS_ROOT)/#{$audio_api_config[:dynamic_frameworks_worklets_dir]}/Common/cpp\"" - ] : []) .join(' '), 'OTHER_LDFLAGS' => %W[ $(inherited) diff --git a/packages/react-native-audio-api/android/build.gradle b/packages/react-native-audio-api/android/build.gradle index 0033a2b24..8305f9c7d 100644 --- a/packages/react-native-audio-api/android/build.gradle +++ b/packages/react-native-audio-api/android/build.gradle @@ -74,39 +74,6 @@ def resolveReactNativeDirectory() { ) } -def resolveReactNativeWorkletsDirectory() { - def rnWorklets = rootProject.subprojects.find { it.name == 'react-native-worklets' } - if (rnWorklets != null) { - return rnWorklets.projectDir - } - - return null; -} - -def validateWorkletsVersion() { - def validationScript = file("${projectDir}/../scripts/validate-worklets-version.js") - if (!validationScript.exists()) { - logger.error("[AudioAPI] Worklets validation script not found at ${validationScript.absolutePath}") - return false - } - - try { - def process = ["node", validationScript.absolutePath].execute() - process.waitForProcessOutput(System.out, System.err) - def exitCode = process.exitValue() - - if (exitCode == 0) { - return true - } else { - logger.warn("[AudioAPI] Worklets version validation failed") - return false - } - } catch (Exception e) { - logger.error("[AudioAPI] Failed to validate worklets version: ${e.message}") - return false - } -} - def toPlatformFileString(String path) { if (Os.isFamily(Os.FAMILY_WINDOWS)) { path = path.replace(File.separatorChar, '/' as char) @@ -123,9 +90,35 @@ static def supportsNamespace() { return (major == 7 && minor >= 3) || major >= 8 } +def getAudioApiVersion() { + def inputFile = file("${projectDir.path}/../package.json") + def json = new groovy.json.JsonSlurper().parseText(inputFile.text) + def packageVersion = json.version + if (packageVersion == null) { + throw new GradleException("[AudioAPI] Cannot find version in package.json") + } + return packageVersion.toString() +} + def reactNativeRootDir = resolveReactNativeDirectory() -def reactNativeWorkletsRootDir = resolveReactNativeWorkletsDirectory() -def isWorkletsAvailable = reactNativeWorkletsRootDir != null && validateWorkletsVersion() +def audioApiPackageDir = project.projectDir.parentFile +def AUDIO_API_VERSION = getAudioApiVersion() + +// --- Prefab extension API (common/cpp/audioapi/EXTENSION_API.md) ---------------- +// Extension packages must include only and link +// react-native-audio-api::react-native-audio-api via find_package(). + +// Set version for prefab +version AUDIO_API_VERSION + +def audioApiCommonCppDir = file("$audioApiPackageDir/common/cpp") +def audioApiAndroidCppDir = file("$projectDir/src/main/cpp") + +// build/prefab-headers/audioapi/... — staged for the react-native-audio-api prefab. +def audioApiPrefabHeadersDir = layout.buildDirectory.dir("prefab-headers").get().asFile + +// Headers staged for the react-native-audio-api prefab (mirrors worklets: include-only). +def audioApiPrefabHeaderIncludes = ["audioapi/**/*.h", "audioapi/**/*.hpp"] def reactProperties = new Properties() file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) } @@ -136,6 +129,8 @@ def IS_NEW_ARCHITECTURE_ENABLED = isNewArchitectureEnabled() def IS_RN_AUDIO_API_FFMPEG_DISABLED = isFFmpegDisabled() def IS_RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED = isStaticExternalLibsDisabled() +apply from: "./fix-prefab.gradle" + android { sourceSets { main { @@ -162,7 +157,6 @@ android { consumerProguardFiles("proguard-rules.pro") buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() - buildConfigField "boolean", "RN_AUDIO_API_ENABLE_WORKLETS", "${isWorkletsAvailable}" buildConfigField "boolean", "RN_AUDIO_API_FFMPEG_DISABLED", isFFmpegDisabled().toString() buildConfigField "boolean", "RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED", isStaticExternalLibsDisabled().toString() @@ -175,16 +169,12 @@ android { "-DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}", "-DANDROID_TOOLCHAIN=clang", "-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeRootDir.path)}", - "-DRN_AUDIO_API_WORKLETS_ENABLED=${isWorkletsAvailable}", "-DIS_NEW_ARCHITECTURE_ENABLED=${IS_NEW_ARCHITECTURE_ENABLED}", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", "-DRN_AUDIO_API_FFMPEG_DISABLED=${IS_RN_AUDIO_API_FFMPEG_DISABLED}", "-DRN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED=${IS_RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED}" ] - if (isWorkletsAvailable) { - cmakeArgs << "-DREACT_NATIVE_WORKLETS_DIR=${toPlatformFileString(reactNativeWorkletsRootDir.path)}" - } arguments = cmakeArgs } } @@ -230,6 +220,14 @@ android { buildFeatures { buildConfig true prefab true + prefabPublishing true + } + + // Publish compatibility/StableAPI.h (+ transitive headers) for extension packages. + prefab { + "react-native-audio-api" { + headers audioApiPrefabHeadersDir.absolutePath + } } buildTypes { @@ -290,24 +288,8 @@ dependencies { implementation 'com.facebook.fbjni:fbjni:0.6.0' implementation 'com.google.oboe:oboe:1.9.3' implementation 'androidx.media:media:1.7.0' - - if (isWorkletsAvailable) { - implementation(rootProject.subprojects.find { it.name == 'react-native-worklets' }) - } } -if (isWorkletsAvailable) { - // Ensure worklets is built before react-native-audio-api - def rnWorkletsProject = rootProject.subprojects.find { it.name == 'react-native-worklets' } - evaluationDependsOn(rnWorkletsProject.path) - - afterEvaluate { - tasks.getByName("buildCMakeDebug").dependsOn(rnWorkletsProject.tasks.getByName("mergeDebugNativeLibs")) - tasks.getByName("buildCMakeRelWithDebInfo").dependsOn(rnWorkletsProject.tasks.getByName("mergeReleaseNativeLibs")) - } -} - - def assertMinimalReactNativeVersion = task assertMinimalReactNativeVersionTask { // If you change the minimal React Native version remember to update Compatibility Table in docs def minimalReactNativeVersion = 76 @@ -328,10 +310,21 @@ task downloadPrebuiltBinaries(type: Exec) { environment 'DISABLE_AUDIOAPI_STATIC_EXTERNAL_LIBS', isStaticExternalLibsDisabled() ? '1' : '0' } +tasks.register('prepareAudioApiHeadersForPrefabs', Copy) { + from(audioApiCommonCppDir) { + include(audioApiPrefabHeaderIncludes) + } + from(audioApiAndroidCppDir) { + include(audioApiPrefabHeaderIncludes) + } + into(audioApiPrefabHeadersDir) +} + // Make preBuild depend on the download task tasks.preBuild { dependsOn assertMinimalReactNativeVersion dependsOn downloadPrebuiltBinaries + dependsOn prepareAudioApiHeadersForPrefabs } task cleanCmakeCache() { diff --git a/packages/react-native-audio-api/android/fix-prefab.gradle b/packages/react-native-audio-api/android/fix-prefab.gradle new file mode 100644 index 000000000..3788e7e7b --- /dev/null +++ b/packages/react-native-audio-api/android/fix-prefab.gradle @@ -0,0 +1,61 @@ +// Ensures prefab publications include the built .so, not just headers. +// Ported from react-native-worklets/android/fix-prefab.gradle.kts. +// +// 1. Makes prefab*ConfigurePackage tasks depend on externalNativeBuild* so the +// native library exists before prefab metadata is generated. +// 2. Touches dependent projects' prefab_config.json so their CMake glue is +// regenerated with this library's .so in the link line. + +tasks.configureEach { task -> + def matcher = (task.name =~ /^prefab(.+)ConfigurePackage$/) + if (matcher.matches()) { + def variantName = matcher.group(1) + task.outputs.upToDateWhen { false } + task.dependsOn("externalNativeBuild${variantName}") + } +} + +afterEvaluate { + def abis = reactNativeArchitectures() + + rootProject.allprojects.each { proj -> + if (proj == rootProject) { + return + } + + def dependsOnThisLib = proj.configurations.any { config -> + config.dependencies.any { dep -> + (dep.group == project.group && dep.name == project.name) || + dep.name == project.name + } + } + if (!dependsOnThisLib && proj != project) { + return + } + + if (!proj.plugins.hasPlugin('com.android.application') && + !proj.plugins.hasPlugin('com.android.library')) { + return + } + + // Touch prefab_config.json in dependent native builds so AGP re-runs the + // prefab CLI and emits a *Config.cmake that links libreact-native-audio-api.so. + // See ExternalNativeJsonGenerator.kt in Android Gradle Plugin. + def cxxDir = new File(proj.projectDir, '.cxx') + if (!cxxDir.exists()) { + return + } + + cxxDir.eachDir { variantDir -> + def randomDirs = variantDir.listFiles({ File f -> f.isDirectory() } as FileFilter) + abis.each { abi -> + randomDirs?.each { randomDir -> + def prefabFile = new File(randomDir, "${abi}/prefab_config.json") + if (prefabFile.exists()) { + prefabFile.setLastModified(System.currentTimeMillis()) + } + } + } + } + } +} diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/CMakeLists.txt b/packages/react-native-audio-api/android/src/main/cpp/audioapi/CMakeLists.txt index accaba6ff..46642e88b 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/CMakeLists.txt +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/CMakeLists.txt @@ -73,24 +73,27 @@ else() set(BUILD_TYPE "release") endif() +# PUBLIC: extension packages consume `` via the published prefab. target_include_directories( react-native-audio-api + PUBLIC + "${COMMON_CPP_DIR}" + "${ANDROID_CPP_DIR}" PRIVATE - "${COMMON_CPP_DIR}" - "${ANDROID_CPP_DIR}" - "${INCLUDE_DIR}" - "${FFMPEG_INCLUDE_DIR}" - "${REACT_NATIVE_DIR}/ReactCommon" - "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule" - "${REACT_NATIVE_DIR}/ReactCommon/callinvoker" + "${INCLUDE_DIR}" + "${FFMPEG_INCLUDE_DIR}" + "${REACT_NATIVE_DIR}/ReactCommon" + "${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor" + "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule" + "${REACT_NATIVE_DIR}/ReactCommon/callinvoker" ) if(NOT RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED) target_include_directories( react-native-audio-api PRIVATE - "${INCLUDE_DIR}/opus" - "${INCLUDE_DIR}/vorbis" + "${INCLUDE_DIR}/opus" + "${INCLUDE_DIR}/vorbis" ) endif() @@ -102,67 +105,10 @@ set(LINK_LIBRARIES oboe::oboe ) -set(INCLUDE_LIBRARIES - "${COMMON_CPP_DIR}" - "${ANDROID_CPP_DIR}" - "${INCLUDE_DIR}" - "${REACT_NATIVE_DIR}/ReactCommon" - "${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor" - "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule" - "${REACT_NATIVE_DIR}/ReactCommon/callinvoker" -) - -if(NOT RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED) - list(APPEND INCLUDE_LIBRARIES - "${INCLUDE_DIR}/opus" - "${INCLUDE_DIR}/vorbis" - ) -endif() - -if(RN_AUDIO_API_WORKLETS_ENABLED) - list(APPEND INCLUDE_LIBRARIES - "${REACT_NATIVE_WORKLETS_DIR}/../Common/cpp" - "${REACT_NATIVE_WORKLETS_DIR}/src/main/cpp" - ) - - find_package(react-native-worklets CONFIG QUIET) - if(react-native-worklets_FOUND) - list(APPEND LINK_LIBRARIES react-native-worklets::worklets) - else() - # Fallback for environments without prefab publication (older worklets) - add_library(worklets SHARED IMPORTED) - set_target_properties( - worklets - PROPERTIES - IMPORTED_LOCATION - "${REACT_NATIVE_WORKLETS_DIR}/build/intermediates/cmake/${BUILD_TYPE}/obj/${ANDROID_ABI}/libworklets.so" - ) - list(APPEND LINK_LIBRARIES worklets) - endif() -endif() - -target_include_directories( - react-native-audio-api - PRIVATE - ${INCLUDE_LIBRARIES} -) - set(RN_VERSION_LINK_LIBRARIES ReactAndroid::reactnative ) -if(RN_AUDIO_API_WORKLETS_ENABLED) - target_compile_definitions( - react-native-audio-api - PRIVATE RN_AUDIO_API_ENABLE_WORKLETS=1 - ) -else() - target_compile_definitions( - react-native-audio-api - PRIVATE RN_AUDIO_API_ENABLE_WORKLETS=0 - ) -endif() - if (RN_AUDIO_API_FFMPEG_DISABLED) target_compile_definitions( react-native-audio-api diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp index d8d9005c6..943c42a5c 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp @@ -9,38 +9,20 @@ using namespace facebook::jni; AudioAPIModule::AudioAPIModule( jni::alias_ref &jThis, -#if RN_AUDIO_API_ENABLE_WORKLETS - std::weak_ptr weakWorkletsModuleProxy, -#endif jsi::Runtime *jsiRuntime, const std::shared_ptr &jsCallInvoker) - : javaPart_(make_global(jThis)), - jsiRuntime_(jsiRuntime), -#if RN_AUDIO_API_ENABLE_WORKLETS - weakWorkletsModuleProxy_(weakWorkletsModuleProxy), -#endif - jsCallInvoker_(jsCallInvoker) { + : javaPart_(make_global(jThis)), jsiRuntime_(jsiRuntime), jsCallInvoker_(jsCallInvoker) { audioEventHandlerRegistry_ = std::make_shared(jsiRuntime, jsCallInvoker); } jni::local_ref AudioAPIModule::initHybrid( jni::alias_ref jThis, - jni::alias_ref jWorkletsModule, jlong jsContext, jni::alias_ref jsCallInvokerHolder) { auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker(); auto rnRuntime = reinterpret_cast(jsContext); -#if RN_AUDIO_API_ENABLE_WORKLETS - if (jWorkletsModule) { - auto castedModule = jni::static_ref_cast(jWorkletsModule); - auto workletsModuleProxy = castedModule->cthis()->getWorkletsModuleProxy(); - return makeCxxInstance(jThis, workletsModuleProxy, rnRuntime, jsCallInvoker); - } - throw std::runtime_error("Worklets module is required but not provided from Java/Kotlin side"); -#else return makeCxxInstance(jThis, rnRuntime, jsCallInvoker); -#endif } void AudioAPIModule::registerNatives() { @@ -57,13 +39,8 @@ void AudioAPIModule::injectJSIBindings() { // cache app directory paths on the attached thread NativeFileInfo::warmCache(); -#if RN_AUDIO_API_ENABLE_WORKLETS - auto uiWorkletRuntime = weakWorkletsModuleProxy_.lock()->getUIWorkletRuntime(); -#else - auto uiWorkletRuntime = nullptr; -#endif AudioAPIModuleInstaller::injectJSIBindings( - jsiRuntime_, jsCallInvoker_, audioEventHandlerRegistry_, uiWorkletRuntime); + jsiRuntime_, jsCallInvoker_, audioEventHandlerRegistry_); } void AudioAPIModule::invokeHandlerWithEventNameAndEventBody( diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/AudioAPIModule.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/AudioAPIModule.h index 9df1882e0..b0719a4db 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/AudioAPIModule.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/AudioAPIModule.h @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -10,7 +9,6 @@ namespace audioapi { using namespace facebook; -using namespace worklets; class AudioAPIModule : public jni::HybridClass { public: @@ -18,8 +16,6 @@ class AudioAPIModule : public jni::HybridClass { static jni::local_ref initHybrid( jni::alias_ref jThis, - jni::alias_ref - jWorkletsModule, // it will be null if RN_AUDIO_API_ENABLE_WORKLETS is false jlong jsContext, jni::alias_ref jsCallInvokerHolder); @@ -35,24 +31,13 @@ class AudioAPIModule : public jni::HybridClass { jni::global_ref javaPart_; jsi::Runtime *jsiRuntime_; -#if RN_AUDIO_API_ENABLE_WORKLETS - std::weak_ptr weakWorkletsModuleProxy_; -#endif std::shared_ptr jsCallInvoker_; std::shared_ptr audioEventHandlerRegistry_; -#if RN_AUDIO_API_ENABLE_WORKLETS explicit AudioAPIModule( jni::alias_ref &jThis, - std::weak_ptr weakWorkletsModuleProxy, jsi::Runtime *jsiRuntime, const std::shared_ptr &jsCallInvoker); -#else - explicit AudioAPIModule( - jni::alias_ref &jThis, - jsi::Runtime *jsiRuntime, - const std::shared_ptr &jsCallInvoker); -#endif }; } // namespace audioapi diff --git a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt index 30865917a..301854ce1 100644 --- a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +++ b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt @@ -39,7 +39,6 @@ class AudioAPIModule( @OptIn(markerClass = [FrameworkAPI::class]) private external fun initHybrid( - workletsModule: Any?, jsContext: Long, callInvoker: CallInvokerHolderImpl, ): HybridData @@ -68,16 +67,7 @@ class AudioAPIModule( val jsContext = context.javaScriptContextHolder!!.get() val jsCallInvokerHolder = context.jsCallInvokerHolder as CallInvokerHolderImpl - var workletsModule: Any? = null - if (BuildConfig.RN_AUDIO_API_ENABLE_WORKLETS) { - try { - workletsModule = context.getNativeModule("WorkletsModule") - } catch (_: Exception) { - throw RuntimeException("WorkletsModule not found - make sure react-native-worklets is properly installed") - } - } - - mHybridData = initHybrid(workletsModule, jsContext, jsCallInvokerHolder) + mHybridData = initHybrid(jsContext, jsCallInvokerHolder) MediaSessionManager.initialize(WeakReference(this), reactContext) NativeFileInfo.initialize(reactContext) injectJSIBindings() diff --git a/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h b/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h index 44be8101b..762c4ce20 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h @@ -15,8 +15,6 @@ #include #include -#include - #include namespace audioapi { @@ -27,14 +25,13 @@ class AudioAPIModuleInstaller { static void injectJSIBindings( jsi::Runtime *jsiRuntime, const std::shared_ptr &jsCallInvoker, - const std::shared_ptr &audioEventHandlerRegistry, - std::shared_ptr uiRuntime = nullptr) { - auto createAudioContext = getCreateAudioContextFunction( - jsiRuntime, jsCallInvoker, audioEventHandlerRegistry, uiRuntime); + const std::shared_ptr &audioEventHandlerRegistry) { + auto createAudioContext = + getCreateAudioContextFunction(jsiRuntime, jsCallInvoker, audioEventHandlerRegistry); auto createAudioRecorder = getCreateAudioRecorderFunction(jsiRuntime, jsCallInvoker, audioEventHandlerRegistry); - auto createOfflineAudioContext = getCreateOfflineAudioContextFunction( - jsiRuntime, jsCallInvoker, audioEventHandlerRegistry, uiRuntime); + auto createOfflineAudioContext = + getCreateOfflineAudioContextFunction(jsiRuntime, jsCallInvoker, audioEventHandlerRegistry); auto createAudioBuffer = getCreateAudioBufferFunction(jsiRuntime); auto createAudioDecoder = getCreateAudioDecoderFunction(jsiRuntime, jsCallInvoker); auto createAudioFileUtils = getCreateAudioFileUtilsFunction(jsiRuntime, jsCallInvoker); @@ -59,30 +56,20 @@ class AudioAPIModuleInstaller { static jsi::Function getCreateAudioContextFunction( jsi::Runtime *jsiRuntime, const std::shared_ptr &jsCallInvoker, - const std::shared_ptr &audioEventHandlerRegistry, - const std::weak_ptr &uiRuntime) { + const std::shared_ptr &audioEventHandlerRegistry) { return jsi::Function::createFromHostFunction( *jsiRuntime, jsi::PropNameID::forAscii(*jsiRuntime, "createAudioContext"), 1, - [jsCallInvoker, audioEventHandlerRegistry, uiRuntime]( + [jsCallInvoker, audioEventHandlerRegistry]( jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value { auto sampleRate = static_cast(args[0].getNumber()); -#if RN_AUDIO_API_ENABLE_WORKLETS - auto runtimeRegistry = RuntimeRegistry{.uiRuntime = uiRuntime}; - if (count > 1 && args[1].isObject()) { - runtimeRegistry.audioRuntime = worklets::extractWorkletRuntime(runtime, args[1]); - } -#else - auto runtimeRegistry = RuntimeRegistry{}; -#endif - auto audioContextHostObject = std::make_shared( - sampleRate, audioEventHandlerRegistry, runtimeRegistry, &runtime, jsCallInvoker); + sampleRate, audioEventHandlerRegistry, &runtime, jsCallInvoker); return jsi::Object::createFromHostObject(runtime, audioContextHostObject); }); @@ -91,13 +78,12 @@ class AudioAPIModuleInstaller { static jsi::Function getCreateOfflineAudioContextFunction( jsi::Runtime *jsiRuntime, const std::shared_ptr &jsCallInvoker, - const std::shared_ptr &audioEventHandlerRegistry, - const std::weak_ptr &uiRuntime) { + const std::shared_ptr &audioEventHandlerRegistry) { return jsi::Function::createFromHostFunction( *jsiRuntime, jsi::PropNameID::forAscii(*jsiRuntime, "createOfflineAudioContext"), 3, - [jsCallInvoker, audioEventHandlerRegistry, uiRuntime]( + [jsCallInvoker, audioEventHandlerRegistry]( jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *args, @@ -106,21 +92,11 @@ class AudioAPIModuleInstaller { auto length = static_cast(args[1].getNumber()); auto sampleRate = static_cast(args[2].getNumber()); -#if RN_AUDIO_API_ENABLE_WORKLETS - auto runtimeRegistry = RuntimeRegistry{.uiRuntime = uiRuntime}; - if (count > 3 && args[3].isObject()) { - runtimeRegistry.audioRuntime = worklets::extractWorkletRuntime(runtime, args[3]); - } -#else - auto runtimeRegistry = RuntimeRegistry{}; -#endif - auto audioContextHostObject = std::make_shared( numberOfChannels, length, sampleRate, audioEventHandlerRegistry, - runtimeRegistry, &runtime, jsCallInvoker); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp index 0ece298cf..1fdc3c820 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp @@ -12,11 +12,10 @@ namespace audioapi { AudioContextHostObject::AudioContextHostObject( float sampleRate, const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry, jsi::Runtime *runtime, const std::shared_ptr &callInvoker) : BaseAudioContextHostObject( - std::make_shared(sampleRate, audioEventHandlerRegistry, runtimeRegistry), + std::make_shared(sampleRate, audioEventHandlerRegistry), runtime, callInvoker) { addFunctions( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.h index ce114618d..99104aa08 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -17,7 +16,6 @@ class AudioContextHostObject : public BaseAudioContextHostObject { explicit AudioContextHostObject( float sampleRate, const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry, jsi::Runtime *runtime, const std::shared_ptr &callInvoker); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp index 671645e33..8c3986785 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include @@ -19,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -46,9 +43,6 @@ BaseAudioContextHostObject::BaseAudioContextHostObject( JSI_EXPORT_PROPERTY_GETTER(BaseAudioContextHostObject, currentTime)); addFunctions( - JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createWorkletSourceNode), - JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createWorkletNode), - JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createWorkletProcessingNode), JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createRecorderAdapter), JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createOscillator), JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createStreamer), @@ -90,68 +84,6 @@ JSI_PROPERTY_GETTER_IMPL(BaseAudioContextHostObject, currentTime) { return {context_->getCurrentTime()}; } -JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWorkletSourceNode) { -#if RN_AUDIO_API_ENABLE_WORKLETS - auto shareableWorklet = - worklets::extractSerializableOrThrow(runtime, args[0]); - auto shouldUseUiRuntime = args[1].getBool(); - std::weak_ptr workletRuntime = shouldUseUiRuntime - ? context_->getRuntimeRegistry().uiRuntime - : context_->getRuntimeRegistry().audioRuntime; - - auto workletSourceNodeHostObject = std::make_shared( - context_->getGraph(), context_, workletRuntime, shareableWorklet, shouldUseUiRuntime); - return jsi::Object::createFromHostObject(runtime, workletSourceNodeHostObject); -#endif - return jsi::Value::undefined(); -} - -JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWorkletNode) { -#if RN_AUDIO_API_ENABLE_WORKLETS - auto shareableWorklet = - worklets::extractSerializableOrThrow(runtime, args[0]); - auto shouldUseUiRuntime = args[1].getBool(); - std::weak_ptr workletRuntime = shouldUseUiRuntime - ? context_->getRuntimeRegistry().uiRuntime - : context_->getRuntimeRegistry().audioRuntime; - auto bufferLength = static_cast(args[2].getNumber()); - auto inputChannelCount = static_cast(args[3].getNumber()); - - auto workletNodeHostObject = std::make_shared( - context_->getGraph(), - context_, - workletRuntime, - shareableWorklet, - shouldUseUiRuntime, - bufferLength, - inputChannelCount); - auto jsiObject = jsi::Object::createFromHostObject(runtime, workletNodeHostObject); - jsiObject.setExternalMemoryPressure( - runtime, - sizeof(float) * bufferLength * inputChannelCount); // rough estimate of underlying buffer - return jsiObject; -#endif - return jsi::Value::undefined(); -} - -JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createWorkletProcessingNode) { -#if RN_AUDIO_API_ENABLE_WORKLETS - auto shareableWorklet = - worklets::extractSerializableOrThrow(runtime, args[0]); - auto shouldUseUiRuntime = args[1].getBool(); - std::weak_ptr workletRuntime = shouldUseUiRuntime - ? context_->getRuntimeRegistry().uiRuntime - : context_->getRuntimeRegistry().audioRuntime; - - auto workletProcessingNodeHostObject = std::make_shared( - context_->getGraph(), context_, workletRuntime, shareableWorklet, shouldUseUiRuntime); - auto object = jsi::Object::createFromHostObject(runtime, workletProcessingNodeHostObject); - object.setExternalMemoryPressure(runtime, workletProcessingNodeHostObject->getMemoryPressure()); - return object; -#endif - return jsi::Value::undefined(); -} - JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createRecorderAdapter) { auto recorderAdapterHostObject = std::make_shared(context_); auto object = jsi::Object::createFromHostObject(runtime, recorderAdapterHostObject); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h index b8d597209..499c7333d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h @@ -27,9 +27,6 @@ class BaseAudioContextHostObject : public HostObject { JSI_PROPERTY_GETTER_DECL(sampleRate); JSI_PROPERTY_GETTER_DECL(currentTime); - JSI_HOST_FUNCTION_DECL(createWorkletSourceNode); - JSI_HOST_FUNCTION_DECL(createWorkletNode); - JSI_HOST_FUNCTION_DECL(createWorkletProcessingNode); JSI_HOST_FUNCTION_DECL(createRecorderAdapter); JSI_HOST_FUNCTION_DECL(createOscillator); JSI_HOST_FUNCTION_DECL(createStreamer); @@ -47,6 +44,12 @@ class BaseAudioContextHostObject : public HostObject { JSI_HOST_FUNCTION_DECL(createWaveShaper); JSI_HOST_FUNCTION_DECL(createDelay); + /// @brief Access the underlying C++ audio context. + /// @return The underlying C++ audio context. + [[nodiscard]] const std::shared_ptr &getContext() const { + return context_; + } + protected: std::shared_ptr context_; std::shared_ptr promiseVendor_; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.cpp index 189f88f93..5644864f4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.cpp @@ -12,7 +12,6 @@ OfflineAudioContextHostObject::OfflineAudioContextHostObject( size_t length, float sampleRate, const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry, jsi::Runtime *runtime, const std::shared_ptr &callInvoker) : BaseAudioContextHostObject( @@ -20,8 +19,7 @@ OfflineAudioContextHostObject::OfflineAudioContextHostObject( numberOfChannels, length, sampleRate, - audioEventHandlerRegistry, - runtimeRegistry), + audioEventHandlerRegistry), runtime, callInvoker) { addFunctions( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.h index 588a41ebe..a2997ee74 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -19,7 +18,6 @@ class OfflineAudioContextHostObject : public BaseAudioContextHostObject { size_t length, float sampleRate, const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry, jsi::Runtime *runtime, const std::shared_ptr &callInvoker); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WorkletNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WorkletNodeHostObject.h deleted file mode 100644 index e3d16ef1a..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WorkletNodeHostObject.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -namespace audioapi { -using namespace facebook; - -class WorkletNodeHostObject : public AudioNodeHostObject { - public: - explicit WorkletNodeHostObject( - const std::shared_ptr &graph, - const std::shared_ptr &context, - std::weak_ptr workletRuntime, - const std::shared_ptr &shareableWorklet, - bool shouldLockRuntime, - size_t bufferLength, - size_t inputChannelCount) - : AudioNodeHostObject( - graph, - std::make_unique( - context, - bufferLength, - inputChannelCount, - WorkletsRunner(workletRuntime, shareableWorklet, shouldLockRuntime))) {} -}; -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WorkletProcessingNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WorkletProcessingNodeHostObject.h deleted file mode 100644 index cdad2dea9..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/effects/WorkletProcessingNodeHostObject.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -namespace audioapi { -using namespace facebook; - -class WorkletProcessingNodeHostObject : public AudioNodeHostObject { - public: - explicit WorkletProcessingNodeHostObject( - const std::shared_ptr &graph, - const std::shared_ptr &context, - std::weak_ptr workletRuntime, - const std::shared_ptr &shareableWorklet, - bool shouldLockRuntime) - : AudioNodeHostObject( - graph, - std::make_unique( - context, - WorkletsRunner(workletRuntime, shareableWorklet, shouldLockRuntime))) {} - - [[nodiscard]] size_t getMemoryPressure() const override { - // 2 input + 2 output AudioArrayBuffer (RQ floats) wrapped as JS typed arrays. - // The worklet closure/captures live in JS and aren't tracked here. - return AudioNodeHostObject::getMemoryPressure() + 4 * RENDER_QUANTUM_SIZE * sizeof(float); - } -}; -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/WorkletSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/WorkletSourceNodeHostObject.h deleted file mode 100644 index a9ae0e484..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/sources/WorkletSourceNodeHostObject.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -namespace audioapi { -using namespace facebook; - -class WorkletSourceNodeHostObject : public AudioScheduledSourceNodeHostObject { - public: - explicit WorkletSourceNodeHostObject( - const std::shared_ptr &graph, - const std::shared_ptr &context, - std::weak_ptr workletRuntime, - const std::shared_ptr &shareableWorklet, - bool shouldLockRuntime, - const AudioScheduledSourceNodeOptions &options = AudioScheduledSourceNodeOptions()) - : AudioScheduledSourceNodeHostObject( - graph, - std::make_unique( - context, - WorkletsRunner(workletRuntime, shareableWorklet, shouldLockRuntime)), - options) {} -}; -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/compatibility/EXTENSION_API.md b/packages/react-native-audio-api/common/cpp/audioapi/compatibility/EXTENSION_API.md new file mode 100644 index 000000000..657643706 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/compatibility/EXTENSION_API.md @@ -0,0 +1,152 @@ +# react-native-audio-api — C++ Extension API + +Extension packages integrate with react-native-audio-api in C++ through a **single** +public header: + +```cpp +#include +``` + +Do **not** include any other `audioapi/...` headers. Types and helpers needed for +extensions are pulled in by `StableAPI.h`. Everything else is internal. + +--- + +## Android (Prefab) + +| Item | Value | +|---|---| +| Gradle module | `:react-native-audio-api` | +| Prefab package | `react-native-audio-api` | +| CMake `find_package` | `find_package(react-native-audio-api REQUIRED CONFIG)` | +| Link target | `react-native-audio-api::react-native-audio-api` | +| Shared library | `libreact-native-audio-api.so` | +| **Only public include** | `` | + +### Example `CMakeLists.txt` + +```cmake +find_package(react-native-audio-api REQUIRED CONFIG) + +target_link_libraries(my-extension + react-native-audio-api::react-native-audio-api + ReactAndroid::jsi + # ... +) +``` + +Prefab ships `audioapi/**/*.{h,hpp}` headers (include-only, same pattern as +react-native-worklets). Extension code must still include only ``. + +--- + +## iOS (CocoaPods) + +| Item | Value | +|---|---| +| Pod | `RNAudioAPI` | +| Podspec dependency | `s.dependency 'RNAudioAPI'` | +| npm peer dependency | `react-native-audio-api` (in extension `package.json`) | +| **Only public include** | `` | + +### Example `*.podspec` + +Extension packages ship their own pod (e.g. `RNAudioWorklets`) and **must** depend on +`RNAudioAPI` so CocoaPods links the native library and exposes public headers: + +```ruby +Pod::Spec.new do |s| + s.name = "MyAudioExtension" + # ... + + s.source_files = "common/cpp/myextension/**/*.{cpp,h}" + s.header_dir = "myextension" + s.header_mappings_dir = "common/cpp/myextension" + + s.dependency 'RNAudioAPI' + s.dependency 'React-jsi' + + s.pod_target_xcconfig = { + "HEADER_SEARCH_PATHS" => [ + '"$(PODS_ROOT)/Headers/Public/RNAudioAPI"', + # React / Folly paths (see react-native-audio-worklets/RNAudioWorklets.podspec)... + ].join(' '), + "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", + } + + install_modules_dependencies(s) +end +``` + +Reference: `packages/react-native-audio-worklets/RNAudioWorklets.podspec`. + +### `pod_target_xcconfig` (header search paths) + +`s.dependency 'RNAudioAPI'` usually adds the RNAudioAPI header search paths +automatically. You may still set this explicitly so `` is +obvious in the podspec: + +```ruby +"HEADER_SEARCH_PATHS" => [ + '"$(PODS_ROOT)/Headers/Public/RNAudioAPI"', + # React / Folly paths... +].join(' ') +``` + +--- + +## API available through `StableAPI.h` + +| Type / header (transitive) | Purpose | +|---|---| +| `audioapi::BaseAudioContextHostObject` | Resolve `AudioContext` from a JSI object via `getContext()` | +| `audioapi::AudioNodeHostObject` | Base HostObject for custom nodes exposed to JS | +| `audioapi::BaseAudioContext` | Context handle; `getGraph()`, scheduling, sample rate | +| `audioapi::AudioNode` | Base class for custom `AudioNode` implementations | +| `audioapi::utils::graph::Graph` | Audio graph owned by the context | +| `RENDER_QUANTUM_SIZE` etc. (via `core/utils/Constants.h`) | Frame-size and engine constants | +| `audioapi::AudioBuffer` | Multi-channel float buffer | +| `audioapi::AudioArrayBuffer` | Single-channel view for JSI handoff | + +--- + +## Integration pattern + +1. **Separate package** with its own pod / pure C++ TurboModule / CMake target. +2. **Declare dependencies:** + - **iOS:** `s.dependency 'RNAudioAPI'` in your podspec (see example above). + - **Android:** `find_package(react-native-audio-api)` + link + `react-native-audio-api::react-native-audio-api` in CMake. + - **npm:** `react-native-audio-api` as a `peerDependency` in `package.json`. +3. **`#include `** — the only audio-api C++ include. +4. **Install JSI bindings** from your own TurboModule (do not modify + `AudioAPIModuleInstaller`). +5. **Resolve the context** from JS: + + ```cpp + auto host = contextObject.getHostObject(runtime); + auto context = host->getContext(); + auto graph = context->getGraph(); + ``` + +6. **Build a custom node** by subclassing `audioapi::AudioNode` and wrapping it in a + HostObject that extends `audioapi::AudioNodeHostObject`, then connect it through the + graph like built-in nodes. + +Reference implementation: `packages/react-native-audio-worklets`. + +--- + +## Explicitly not supported + +- Any `#include` other than `` +- `audioapi/AudioAPIModuleInstaller.h` — core global install; not for extensions +- `audioapi/libs/**`, `audioapi/dsp/**`, `audioapi/external/**` +- Concrete built-in node HostObjects — reference only, not a stable base API + +--- + +## Versioning + +Declare a `react-native-audio-api` **peer dependency** in `package.json` with a +semver range matching the API you target. diff --git a/packages/react-native-audio-api/common/cpp/audioapi/compatibility/StableAPI.h b/packages/react-native-audio-api/common/cpp/audioapi/compatibility/StableAPI.h new file mode 100644 index 000000000..83348d4f5 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/compatibility/StableAPI.h @@ -0,0 +1,17 @@ +#pragma once + +/** + * Stable C++ compatibility API for react-native-audio-api. + * + * Extension packages must include **only** this header — do not include other + * `audioapi/...` headers directly. Symbols not reachable through this file are + * internal and may change without notice. + */ + +#include +#include +#include +#include +#include +#include +#include diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp index 77dc9d351..9b2111ec8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp @@ -12,10 +12,8 @@ namespace audioapi { AudioContext::AudioContext( float sampleRate, - const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry) - : BaseAudioContext(sampleRate, audioEventHandlerRegistry, runtimeRegistry), - isInitialized_(false) {} + const std::shared_ptr &audioEventHandlerRegistry) + : BaseAudioContext(sampleRate, audioEventHandlerRegistry), isInitialized_(false) {} AudioContext::~AudioContext() { if (getState() != ContextState::CLOSED) { diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h index 88ec77584..7b4fcd65b 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -20,8 +19,7 @@ class AudioContext : public BaseAudioContext { public: explicit AudioContext( float sampleRate, - const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry); + const std::shared_ptr &audioEventHandlerRegistry); ~AudioContext() override; DELETE_COPY_AND_MOVE(AudioContext); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp index 9e62f92b2..117856b41 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -15,12 +14,10 @@ namespace audioapi { BaseAudioContext::BaseAudioContext( float sampleRate, - const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry) + const std::shared_ptr &audioEventHandlerRegistry) : state_(ContextState::SUSPENDED), sampleRate_(sampleRate), audioEventHandlerRegistry_(audioEventHandlerRegistry), - runtimeRegistry_(runtimeRegistry), audioEventScheduler_(AUDIO_SCHEDULER_CAPACITY), gcAudioEventScheduler_(GC_AUDIO_SCHEDULER_CAPACITY), disposer_( @@ -100,10 +97,6 @@ std::shared_ptr BaseAudioContext::getAudioEventHandl return audioEventHandlerRegistry_; } -const RuntimeRegistry &BaseAudioContext::getRuntimeRegistry() const { - return runtimeRegistry_; -} - utils::DisposerImpl *BaseAudioContext::getDisposer() const { return disposer_.get(); } diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h index 8c489637a..8773f0adc 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -30,8 +29,7 @@ class BaseAudioContext : public std::enable_shared_from_this { public: explicit BaseAudioContext( float sampleRate, - const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry); + const std::shared_ptr &audioEventHandlerRegistry); virtual ~BaseAudioContext() = default; DELETE_COPY_AND_MOVE(BaseAudioContext); @@ -53,7 +51,6 @@ class BaseAudioContext : public std::enable_shared_from_this { std::shared_ptr getBasicWaveForm(OscillatorType type); std::shared_ptr getGraph() const; std::shared_ptr getAudioEventHandlerRegistry() const; - const RuntimeRegistry &getRuntimeRegistry() const; utils::DisposerImpl *getDisposer() const; /// @brief Assigns the audio destination node to the context. @@ -124,7 +121,6 @@ class BaseAudioContext : public std::enable_shared_from_this { private: std::atomic sampleRate_; std::shared_ptr audioEventHandlerRegistry_; - RuntimeRegistry runtimeRegistry_; std::shared_ptr cachedSineWave_ = nullptr; std::shared_ptr cachedSquareWave_ = nullptr; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp index ccc66c7f1..eff37345d 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.cpp @@ -19,9 +19,8 @@ OfflineAudioContext::OfflineAudioContext( int numberOfChannels, size_t length, float sampleRate, - const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry) - : BaseAudioContext(sampleRate, audioEventHandlerRegistry, runtimeRegistry), + const std::shared_ptr &audioEventHandlerRegistry) + : BaseAudioContext(sampleRate, audioEventHandlerRegistry), length_(length), currentSampleFrame_(0), audioBuffer_( diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h index d7872f2b7..542173de1 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/OfflineAudioContext.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -19,8 +18,7 @@ class OfflineAudioContext : public BaseAudioContext { int numberOfChannels, size_t length, float sampleRate, - const std::shared_ptr &audioEventHandlerRegistry, - const RuntimeRegistry &runtimeRegistry); + const std::shared_ptr &audioEventHandlerRegistry); ~OfflineAudioContext() override = default; DELETE_COPY_AND_MOVE(OfflineAudioContext); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp deleted file mode 100644 index 7c034abbd..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include -#include - -namespace audioapi { - -WorkletNode::WorkletNode( - const std::shared_ptr &context, - size_t bufferLength, - size_t inputChannelCount, - WorkletsRunner &&workletRunner) - : AudioNode(context), - workletRunner_(std::move(workletRunner)), - buffer_( - std::make_shared(bufferLength, inputChannelCount, context->getSampleRate())), - bufferLength_(bufferLength), - inputChannelCount_(inputChannelCount), - curBuffIndex_(0) {} - -void WorkletNode::processNode(int framesToProcess) { - int processed = 0; - size_t channelCount_ = - std::min(inputChannelCount_, static_cast(audioBuffer_->getNumberOfChannels())); - while (processed < framesToProcess) { - size_t framesToWorkletInvoke = bufferLength_ - curBuffIndex_; - size_t needsToProcess = framesToProcess - processed; - size_t shouldProcess = std::min(framesToWorkletInvoke, needsToProcess); - - /// here we copy - /// to [curBuffIndex_, curBuffIndex_ + shouldProcess] - /// from [processed, processed + shouldProcess] - buffer_->copy(*audioBuffer_, processed, curBuffIndex_, shouldProcess); - - processed += static_cast(shouldProcess); - curBuffIndex_ += shouldProcess; - - /// If we filled the entire buffer, we need to execute the worklet - if (curBuffIndex_ != bufferLength_) { - continue; - } - // Reset buffer index, channel buffers and execute worklet - curBuffIndex_ = 0; - workletRunner_.executeOnRuntimeSync([this, channelCount_](jsi::Runtime &uiRuntimeRaw) { - /// Arguments preparation - auto jsArray = jsi::Array(uiRuntimeRaw, channelCount_); - for (size_t ch = 0; ch < channelCount_; ch++) { - auto sharedAudioArray = std::make_shared(bufferLength_); - sharedAudioArray->copy(*buffer_->getChannel(ch)); - auto sharedAudioArraySize = sharedAudioArray->size(); - auto arrayBuffer = jsi::ArrayBuffer(uiRuntimeRaw, std::move(sharedAudioArray)); - arrayBuffer.setExternalMemoryPressure(uiRuntimeRaw, sharedAudioArraySize); - jsArray.setValueAtIndex(uiRuntimeRaw, ch, std::move(arrayBuffer)); - } - - buffer_->zero(); - - /// Call the worklet - workletRunner_.callUnsafe( - std::move(jsArray), jsi::Value(uiRuntimeRaw, static_cast(channelCount_))); - - return jsi::Value::undefined(); - }); - } -} - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h deleted file mode 100644 index df0bc61b6..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletNode.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace audioapi { - -#if RN_AUDIO_API_TEST -class WorkletNode : public AudioNode { - public: - explicit WorkletNode( - std::shared_ptr context, - size_t bufferLength, - size_t inputChannelCount, - WorkletsRunner &&workletRunner) - : AudioNode(context) {} - - protected: - void processNode(int framesToProcess) override {} -}; -#else - -using namespace facebook; - -class WorkletNode : public AudioNode { - public: - explicit WorkletNode( - const std::shared_ptr &context, - size_t bufferLength, - size_t inputChannelCount, - WorkletsRunner &&workletRunner); - DELETE_COPY_AND_MOVE(WorkletNode); - ~WorkletNode() override = default; - - protected: - void processNode(int framesToProcess) override; - - private: - WorkletsRunner workletRunner_; - std::shared_ptr buffer_; - - /// @brief Length of the byte buffer that will be passed to the AudioArrayBuffer - size_t bufferLength_; - size_t inputChannelCount_; - size_t curBuffIndex_; -}; - -#endif // RN_AUDIO_API_TEST - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp deleted file mode 100644 index 317973277..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include -#include -#include - -namespace audioapi { - -WorkletProcessingNode::WorkletProcessingNode( - const std::shared_ptr &context, - WorkletsRunner &&workletRunner) - : AudioNode(context), workletRunner_(std::move(workletRunner)) { - // Pre-allocate buffers for max 128 frames and 2 channels (stereo) - size_t maxChannelCount = 2; - inputBuffsHandles_.resize(maxChannelCount); - outputBuffsHandles_.resize(maxChannelCount); - - for (size_t i = 0; i < maxChannelCount; ++i) { - inputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); - outputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); - } -} - -void WorkletProcessingNode::processNode(int framesToProcess) { - size_t channelCount = std::min( - static_cast(2), // Fixed to stereo for now - audioBuffer_->getNumberOfChannels()); - - // Copy input data to pre-allocated input buffers - for (size_t ch = 0; ch < channelCount; ch++) { - inputBuffsHandles_[ch]->copy(*audioBuffer_->getChannel(ch), 0, 0, framesToProcess); - } - - // Execute the worklet - auto result = workletRunner_.executeOnRuntimeSync( - [this, channelCount, framesToProcess](jsi::Runtime &rt) -> jsi::Value { - auto inputJsArray = jsi::Array(rt, channelCount); - auto outputJsArray = jsi::Array(rt, channelCount); - - for (size_t ch = 0; ch < channelCount; ch++) { - // Create input array buffer - auto inputArrayBuffer = jsi::ArrayBuffer(rt, inputBuffsHandles_[ch]); - inputJsArray.setValueAtIndex(rt, ch, inputArrayBuffer); - - // Create output array buffer - auto outputArrayBuffer = jsi::ArrayBuffer(rt, outputBuffsHandles_[ch]); - outputJsArray.setValueAtIndex(rt, ch, outputArrayBuffer); - } - - // We call unsafely here because we are already on the runtime thread - // and the runtime is locked by executeOnRuntimeSync (if - // shouldLockRuntime is true) - double time = 0.0f; - if (std::shared_ptr context = context_.lock()) { - time = context->getCurrentTime(); - } - return workletRunner_.callUnsafe( - inputJsArray, - outputJsArray, - jsi::Value(rt, static_cast(framesToProcess)), - jsi::Value(rt, time)); - }); - - // Copy processed output data back to the processing buffer or zero on failure - for (size_t ch = 0; ch < channelCount; ch++) { - auto *channelData = audioBuffer_->getChannel(ch); - - if (result.has_value()) { - // Copy processed output data - channelData->copy(*outputBuffsHandles_[ch], 0, 0, framesToProcess); - } else { - // Zero the output on worklet execution failure - channelData->zero(0, framesToProcess); - } - } -} - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h deleted file mode 100644 index 6e917c7c9..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace audioapi { - -#if RN_AUDIO_API_TEST -class WorkletProcessingNode : public AudioNode { - public: - explicit WorkletProcessingNode( - std::shared_ptr context, - WorkletsRunner &&workletRunner) - : AudioNode(context) {} - - protected: - void processNode(int framesToProcess) override {} -}; -#else - -using namespace facebook; - -class WorkletProcessingNode : public AudioNode { - public: - explicit WorkletProcessingNode( - const std::shared_ptr &context, - WorkletsRunner &&workletRunner); - - protected: - void processNode(int framesToProcess) override; - - private: - WorkletsRunner workletRunner_; - std::vector> inputBuffsHandles_; - std::vector> outputBuffsHandles_; -}; - -#endif // RN_AUDIO_API_TEST - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp deleted file mode 100644 index c650487aa..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include -#include - -namespace audioapi { - -WorkletSourceNode::WorkletSourceNode( - const std::shared_ptr &context, - WorkletsRunner &&workletRunner) - : AudioScheduledSourceNode(context), workletRunner_(std::move(workletRunner)) { - // Prepare buffers for audio processing - size_t outputChannelCount = this->getChannelCount(); - outputBuffsHandles_.resize(outputChannelCount); - for (size_t i = 0; i < outputChannelCount; ++i) { - outputBuffsHandles_[i] = std::make_shared(RENDER_QUANTUM_SIZE); - } -} - -void WorkletSourceNode::processNode(int framesToProcess) { - if (isUnscheduled() || isFinished()) { - audioBuffer_->zero(); - return; - } - - size_t startOffset = 0; - size_t nonSilentFramesToProcess = framesToProcess; - - std::shared_ptr context = context_.lock(); - if (context == nullptr) { - audioBuffer_->zero(); - return; - } - updatePlaybackInfo( - audioBuffer_, - framesToProcess, - startOffset, - nonSilentFramesToProcess, - context->getSampleRate(), - context->getCurrentSampleFrame()); - - if (!isPlaying() && !isStopScheduled() || nonSilentFramesToProcess == 0) { - audioBuffer_->zero(); - return; - } - - size_t outputChannelCount = audioBuffer_->getNumberOfChannels(); - - auto result = workletRunner_.executeOnRuntimeSync( - [this, nonSilentFramesToProcess, startOffset, time = context->getCurrentTime()]( - jsi::Runtime &rt) { - auto jsiArray = jsi::Array(rt, this->outputBuffsHandles_.size()); - for (size_t i = 0; i < this->outputBuffsHandles_.size(); ++i) { - auto arrayBuffer = jsi::ArrayBuffer(rt, this->outputBuffsHandles_[i]); - jsiArray.setValueAtIndex(rt, i, arrayBuffer); - } - - // We call unsafely here because we are already on the runtime thread - // and the runtime is locked by executeOnRuntimeSync (if - // shouldLockRuntime is true) - return workletRunner_.callUnsafe( - jsiArray, - jsi::Value(rt, static_cast(nonSilentFramesToProcess)), - jsi::Value(rt, time), - jsi::Value(rt, static_cast(startOffset))); - }); - - // If the worklet execution failed, zero the output - // It might happen if the runtime is not available - if (!result.has_value()) { - audioBuffer_->zero(); - return; - } - - // Copy the processed data back to the AudioBuffer - for (size_t i = 0; i < outputChannelCount; ++i) { - audioBuffer_->getChannel(i)->copy( - *outputBuffsHandles_[i], 0, startOffset, nonSilentFramesToProcess); - } - - handleStopScheduled(); -} - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h deleted file mode 100644 index c22d28ad2..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/sources/WorkletSourceNode.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace audioapi { - -#if RN_AUDIO_API_TEST -class WorkletSourceNode : public AudioScheduledSourceNode { - public: - explicit WorkletSourceNode( - std::shared_ptr context, - WorkletsRunner &&workletRunner) - : AudioScheduledSourceNode(context) {} - - protected: - void processNode(int framesToProcess) override {} -}; -#else - -class WorkletSourceNode : public AudioScheduledSourceNode { - public: - explicit WorkletSourceNode( - const std::shared_ptr &context, - WorkletsRunner &&workletRunner); - - protected: - void processNode(int framesToProcess) override; - - private: - WorkletsRunner workletRunner_; - std::vector> outputBuffsHandles_; -}; -#endif // RN_AUDIO_API_TEST - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h deleted file mode 100644 index 6569e6dc5..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include - -#include -#include - -#if ANDROID -#include -#endif - -#ifndef RN_AUDIO_API_TEST -#define RN_AUDIO_API_TEST 0 -#endif - -#if RN_AUDIO_API_ENABLE_WORKLETS -#include -#include -#include -#if ANDROID -#include -#endif -#else - -#define RN_AUDIO_API_WORKLETS_DISABLED_ERROR \ - std::runtime_error( \ - "Worklets are disabled. Please install react-native-worklets or check if you have supported version to enable these features."); - -/// @brief Dummy implementation of worklets for non-worklet builds they should do nothing and mock necessary methods -/// @note It helps to reduce compile time branching across codebase -/// @note If you need to base some c++ implementation on if the worklets are enabled use `#if RN_AUDIO_API_ENABLE_WORKLETS` -namespace worklets { - -using namespace facebook; -class MessageQueueThread {}; -class WorkletsModuleProxy {}; -class WorkletRuntime { - public: - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) -- dummy type, members unused - explicit WorkletRuntime( - uint64_t, - const std::shared_ptr &, - const std::string &, - const bool) { - throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR - } - [[nodiscard]] jsi::Runtime &getJSIRuntime() const { - throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR - } - [[nodiscard]] jsi::Value executeSync(jsi::Runtime &rt, const jsi::Value &worklet) const { - throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR - } - [[nodiscard]] jsi::Value executeSync(std::function &&job) const { - // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) - throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR} jsi::Value - executeSync(const std::function &job) const { - throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR - } -}; -class SerializableWorklet { - public: - SerializableWorklet(jsi::Runtime *, const jsi::Object &){ - throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR} jsi::Value toJSValue(jsi::Runtime &rt) { - throw RN_AUDIO_API_WORKLETS_DISABLED_ERROR - } -}; -} // namespace worklets - -#undef RN_AUDIO_API_WORKLETS_DISABLED_ERROR - -#endif - -/// @brief Struct to hold references to different runtimes used in the AudioAPI -/// @note it is used to pass them around and avoid creating multiple instances of the same runtime -struct - RuntimeRegistry { // NOLINT(cppcoreguidelines-pro-type-member-init) -- weak_ptr/shared_ptr default-init - std::weak_ptr uiRuntime; - std::shared_ptr audioRuntime; - -#if ANDROID - ~RuntimeRegistry() { - facebook::jni::ThreadScope::WithClassLoader([this]() { - uiRuntime.reset(); - audioRuntime.reset(); - }); - } -#endif -}; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.cpp deleted file mode 100644 index 3e4300c50..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include - -namespace audioapi { - -WorkletsRunner::WorkletsRunner( - std::weak_ptr weakRuntime, - const std::shared_ptr &shareableWorklet, - bool shouldLockRuntime) - : weakRuntime_(std::move(weakRuntime)), shouldLockRuntime(shouldLockRuntime) { - auto strongRuntime = weakRuntime_.lock(); - if (strongRuntime == nullptr) { - return; - } -#if RN_AUDIO_API_ENABLE_WORKLETS - unsafeRuntimePtr = &strongRuntime->getJSIRuntime(); - strongRuntime->executeSync([this, shareableWorklet](jsi::Runtime &rt) -> jsi::Value { - /// Placement new to avoid dynamic memory allocation - new (reinterpret_cast(&unsafeWorklet)) - jsi::Function(shareableWorklet->toJSValue(*unsafeRuntimePtr) - .asObject(*unsafeRuntimePtr) - .asFunction(*unsafeRuntimePtr)); - return jsi::Value::undefined(); - }); - workletInitialized = true; -#else - unsafeRuntimePtr = nullptr; - workletInitialized = false; -#endif -} - -WorkletsRunner::WorkletsRunner(WorkletsRunner &&other) - : weakRuntime_(std::move(other.weakRuntime_)), - unsafeRuntimePtr(other.unsafeRuntimePtr), - workletInitialized(other.workletInitialized), - shouldLockRuntime(other.shouldLockRuntime) { - if (workletInitialized) { - std::memcpy(&unsafeWorklet, &other.unsafeWorklet, sizeof(unsafeWorklet)); - other.workletInitialized = false; - other.unsafeRuntimePtr = nullptr; - } -} - -WorkletsRunner::~WorkletsRunner() { - if (!workletInitialized) { - return; - } - auto strongRuntime = weakRuntime_.lock(); - if (strongRuntime == nullptr) { - // We cannot safely destroy the worklet without a valid runtime - return; - } - reinterpret_cast(&unsafeWorklet)->~Function(); - workletInitialized = false; -} - -std::optional WorkletsRunner::executeOnRuntimeGuarded( - const std::function &&job) const noexcept(noexcept(job)) { - auto strongRuntime = weakRuntime_.lock(); - if (strongRuntime == nullptr) { - return std::nullopt; - } -#if RN_AUDIO_API_ENABLE_WORKLETS - return strongRuntime->executeSync(std::move(job)); -#else - return std::nullopt; -#endif -} - -std::optional WorkletsRunner::executeOnRuntimeUnsafe( - const std::function &&job) const noexcept(noexcept(job)) { -#if RN_AUDIO_API_ENABLE_WORKLETS - return job(*unsafeRuntimePtr); -#else - return std::nullopt; -#endif -} - -}; // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.h b/packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.h deleted file mode 100644 index ed366ddca..000000000 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/utils/worklets/WorkletsRunner.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include - -namespace audioapi { -using namespace facebook; - -/* -* # How to extract worklet from JavaScript argument -* -* To extract a shareable worklet from a JavaScript argument, use the following code: -* -* ```cpp -* auto worklet = worklets::extractSerializableWorkletFromArg(runtime, args[0]); -* ``` -* -* This will return a shared pointer to the extracted worklet, or throw an error if the argument is invalid. -*/ - -class WorkletsRunner { - public: - explicit WorkletsRunner( - std::weak_ptr weakRuntime, - const std::shared_ptr &shareableWorklet, - bool shouldLockRuntime = true); - WorkletsRunner(WorkletsRunner &&); - ~WorkletsRunner(); - - /// @brief Call the worklet function with the given arguments. - /// @tparam ...Args - /// @param ...args - /// @return The result of the worklet function call. - /// @note This method is unsafe and should be used with caution. It assumes that the runtime and worklet are valid and runtime is locked. - template - inline jsi::Value callUnsafe(Args &&...args) { - return getUnsafeWorklet().call(*unsafeRuntimePtr, std::forward(args)...); - } - - /// @brief Call the worklet function with the given arguments. - /// @tparam ...Args - /// @param ...args - /// @return The result of the worklet function call. - /// @note This method is safe and will check if the runtime is available before calling the worklet. If the runtime is not available, it will return nullopt. - template - inline std::optional call(Args &&...args) { - return executeOnRuntimeGuarded([this, args...](jsi::Runtime &rt) -> jsi::Value { - return callUnsafe(std::forward(args)...); - }); - } - - /// @brief Execute a job on the UI runtime safely. - /// @param job - /// @return nullopt if the runtime is not available or the result of the job execution - /// @note Execution is synchronous and will be guarded if shouldLockRuntime is true. - inline std::optional executeOnRuntimeSync( - const std::function &&job) const noexcept(noexcept(job)) { - if (shouldLockRuntime) - return executeOnRuntimeGuarded(std::move(job)); - else - return executeOnRuntimeUnsafe(std::move(job)); - } - - private: - std::weak_ptr weakRuntime_; - jsi::Runtime *unsafeRuntimePtr = nullptr; - - /// @note We want to avoid automatic destruction as - /// when runtime is destroyed, underlying pointer will be invalid - char unsafeWorklet[sizeof(jsi::Function)]; - bool workletInitialized = false; - bool shouldLockRuntime = true; - - inline jsi::Function &getUnsafeWorklet() { - return *reinterpret_cast(&unsafeWorklet); - } - - std::optional executeOnRuntimeGuarded( - const std::function &&job) const noexcept(noexcept(job)); - - std::optional executeOnRuntimeUnsafe( - const std::function &&job) const noexcept(noexcept(job)); -}; - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt index f8a3da551..d2c329b85 100644 --- a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt +++ b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt @@ -50,19 +50,6 @@ target_include_directories( "/opt/homebrew/Cellar/googletest/1.17.0/include" ) -add_library(worklets SHARED IMPORTED) -set_target_properties( - worklets - PROPERTIES - IMPORTED_LOCATION - "${REACT_NATIVE_WORKLETS_DIR}/build/intermediates/cmake/${BUILD_TYPE}/obj/${ANDROID_ABI}/libworklets.so" -) -list(APPEND INCLUDE_LIBRARIES - "${REACT_NATIVE_WORKLETS_DIR}/../Common/cpp" - "${REACT_NATIVE_WORKLETS_DIR}/src/main/cpp" -) -list(APPEND LINK_LIBRARIES worklets) - target_include_directories(rnaudioapi_cursor PUBLIC ${INCLUDE_LIBRARIES}) target_link_libraries(rnaudioapi_cursor ${LINK_LIBRARIES}) diff --git a/packages/react-native-audio-api/common/cpp/test/CMakeLists.txt b/packages/react-native-audio-api/common/cpp/test/CMakeLists.txt index 45f64822a..4ac9fd1df 100644 --- a/packages/react-native-audio-api/common/cpp/test/CMakeLists.txt +++ b/packages/react-native-audio-api/common/cpp/test/CMakeLists.txt @@ -37,8 +37,6 @@ file(GLOB_RECURSE RNAUDIOAPI_SRC # exclude HostObjects from tests list(FILTER RNAUDIOAPI_SRC EXCLUDE REGEX ".*/audioapi/HostObjects/.*\\.cpp$") -# exclude worklet nodes -list(FILTER RNAUDIOAPI_SRC EXCLUDE REGEX ".*/Worklet.*Node\\.cpp$") list(REMOVE_ITEM RNAUDIOAPI_SRC "${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/core/AudioContext.cpp" @@ -89,7 +87,6 @@ add_executable( ${test_src} ) -add_compile_definitions(RN_AUDIO_API_ENABLE_WORKLETS=0) add_compile_definitions(RN_AUDIO_API_TEST=1) add_compile_definitions(RN_AUDIO_API_FFMPEG_DISABLED=1) add_compile_definitions(RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED=1) diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/AudioParamTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/AudioParamTest.cpp index 5062cded2..b1c29471e 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/AudioParamTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/AudioParamTest.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -17,8 +16,7 @@ class AudioParamTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); } }; diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/DelayTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/DelayTest.cpp index 389a860a6..fe055851a 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/DelayTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/DelayTest.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -24,8 +23,7 @@ class DelayTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); destination = std::make_shared(context); context->initialize(destination.get()); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/GainTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/GainTest.cpp index 3ddac4a25..6f3a6d43c 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/GainTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/GainTest.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -22,8 +21,7 @@ class GainTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); destination = std::make_shared(context); context->initialize(destination.get()); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/IIRFilterTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/IIRFilterTest.cpp index 1b400f71a..269b0f779 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/IIRFilterTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/IIRFilterTest.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -23,8 +22,7 @@ class IIRFilterTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); } static std::complex diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/StereoPannerTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/StereoPannerTest.cpp index dff1d86d5..4bf8bda72 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/StereoPannerTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/StereoPannerTest.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -22,8 +21,7 @@ class StereoPannerTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); destination = std::make_shared(context); context->initialize(destination.get()); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/TailProcessingTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/TailProcessingTest.cpp index 24f872759..ece8e9e3d 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/TailProcessingTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/TailProcessingTest.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -75,8 +74,7 @@ class TailProcessingTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * kSampleRate, kSampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * kSampleRate, kSampleRate, eventRegistry); destination = std::make_shared(context); context->initialize(destination.get()); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp index 3666c93b9..f549004a3 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -21,8 +20,7 @@ class WaveShaperNodeTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); } }; diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h index 17fe99703..dd6cf0b1f 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h +++ b/packages/react-native-audio-api/common/cpp/test/src/core/effects/biquad/BiquadFilterTest.h @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -19,8 +18,7 @@ class BiquadFilterTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); } void expectCoefficientsNear( diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/sources/AudioBufferQueueSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/sources/AudioBufferQueueSourceTest.cpp index f61605c0f..354cf24db 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/sources/AudioBufferQueueSourceTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/sources/AudioBufferQueueSourceTest.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -61,8 +60,7 @@ class AudioBufferQueueSourceTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 1, 5 * SAMPLE_RATE, SAMPLE_RATE, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(1, 5 * SAMPLE_RATE, SAMPLE_RATE, eventRegistry); destination = std::make_shared(context); context->initialize(destination.get()); } @@ -162,8 +160,8 @@ TEST_F(AudioBufferQueueSourceTest, StreamingEnqueueKeepsPaceAndContinuity) { /// POSITIONS must still land every 1000 output frames if consumption pace is /// exactly 1x, and every other sample must stay silent. TEST_F(AudioBufferQueueSourceTest, FullGraphRenderPreservesPaceAndSilence) { - auto stereoContext = std::make_shared( - 2, 5 * SAMPLE_RATE, SAMPLE_RATE, eventRegistry, RuntimeRegistry{}); + auto stereoContext = + std::make_shared(2, 5 * SAMPLE_RATE, SAMPLE_RATE, eventRegistry); auto graph = stereoContext->getGraph(); diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/sources/AudioScheduledSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/sources/AudioScheduledSourceTest.cpp index e47203fa7..2cb41e0e9 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/sources/AudioScheduledSourceTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/sources/AudioScheduledSourceTest.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -24,8 +23,7 @@ class AudioScheduledSourceTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); destination = std::make_shared(context); context->initialize(destination.get()); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/sources/ConstantSourceTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/sources/ConstantSourceTest.cpp index c1ce53543..9ad111781 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/sources/ConstantSourceTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/sources/ConstantSourceTest.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -22,8 +21,7 @@ class ConstantSourceTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); destination = std::make_shared(context); context->initialize(destination.get()); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/sources/MediaElementAudioSourceNodeTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/sources/MediaElementAudioSourceNodeTest.cpp index e359bcfe8..62b871e15 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/sources/MediaElementAudioSourceNodeTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/sources/MediaElementAudioSourceNodeTest.cpp @@ -75,8 +75,7 @@ class MediaElementAudioSourceNodeTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * kSampleRate, kSampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * kSampleRate, kSampleRate, eventRegistry); destination = std::make_shared(context); context->initialize(destination.get()); } diff --git a/packages/react-native-audio-api/common/cpp/test/src/core/sources/OscillatorTest.cpp b/packages/react-native-audio-api/common/cpp/test/src/core/sources/OscillatorTest.cpp index d7e2f9feb..4afefa130 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/core/sources/OscillatorTest.cpp +++ b/packages/react-native-audio-api/common/cpp/test/src/core/sources/OscillatorTest.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -20,8 +19,7 @@ class OscillatorTest : public ::testing::Test { void SetUp() override { eventRegistry = std::make_shared(); - context = std::make_shared( - 2, 5 * sampleRate, sampleRate, eventRegistry, RuntimeRegistry{}); + context = std::make_shared(2, 5 * sampleRate, sampleRate, eventRegistry); } }; diff --git a/packages/react-native-audio-api/common/cpp/test/src/graph/TestGraphUtils.h b/packages/react-native-audio-api/common/cpp/test/src/graph/TestGraphUtils.h index ca3f42d53..3783f1b0c 100644 --- a/packages/react-native-audio-api/common/cpp/test/src/graph/TestGraphUtils.h +++ b/packages/react-native-audio-api/common/cpp/test/src/graph/TestGraphUtils.h @@ -30,8 +30,7 @@ namespace audioapi::utils::graph { inline std::shared_ptr getGraphTestContext() { static std::shared_ptr context = [] { auto eventRegistry = std::make_shared(); - auto ctx = - std::make_shared(2, 1024, 44100.0f, eventRegistry, RuntimeRegistry{}); + auto ctx = std::make_shared(2, 1024, 44100.0f, eventRegistry); return ctx; }(); return context; diff --git a/packages/react-native-audio-api/ios/audioapi/ios/AudioAPIModule.mm b/packages/react-native-audio-api/ios/audioapi/ios/AudioAPIModule.mm index 33da72ef3..12206d697 100644 --- a/packages/react-native-audio-api/ios/audioapi/ios/AudioAPIModule.mm +++ b/packages/react-native-audio-api/ios/audioapi/ios/AudioAPIModule.mm @@ -1,16 +1,6 @@ #import #import -#import -#if RN_AUDIO_API_ENABLE_WORKLETS -// Forward-declare the single method used from WorkletsModule instead of importing -// , which transitively includes the codegen-generated -// that is not visible to this compile unit in all build setups -// (e.g. prebuilt RNWorklets in EAS builds). -@interface WorkletsModule : NSObject -- (std::shared_ptr)getWorkletsModuleProxy; -@end -#endif #ifdef RCT_NEW_ARCH_ENABLED #import #endif // RCT_NEW_ARCH_ENABLED @@ -24,7 +14,6 @@ @interface WorkletsModule : NSObject using namespace audioapi; using namespace facebook::react; -using namespace worklets; @interface RCTBridge (JSIRuntime) - (void *)runtime; @@ -41,7 +30,6 @@ - (void)_tryAndHandleError:(dispatch_block_t)block; @implementation AudioAPIModule { std::shared_ptr _eventHandler; - std::weak_ptr weakWorkletsModuleProxy_; } - (void)handleSessionDeactivation @@ -93,33 +81,7 @@ - (dispatch_queue_t)methodQueue _eventHandler = std::make_shared(jsiRuntime, jsCallInvoker); -#if RN_AUDIO_API_ENABLE_WORKLETS - WorkletsModule *workletsModule = [_moduleRegistry moduleForName:"WorkletsModule"]; - - if (!workletsModule) { - NSLog(@"WorkletsModule not found in module registry"); - } - - auto workletsModuleProxy = [workletsModule getWorkletsModuleProxy]; - - if (!workletsModuleProxy) { - NSLog(@"WorkletsModuleProxy not available"); - } - - weakWorkletsModuleProxy_ = workletsModuleProxy; - - auto uiWorkletRuntime = workletsModuleProxy->getUIWorkletRuntime(); - - if (!uiWorkletRuntime) { - NSLog(@"UI Worklet Runtime not available"); - } - - // Get the actual JSI Runtime reference - audioapi::AudioAPIModuleInstaller::injectJSIBindings( - jsiRuntime, jsCallInvoker, _eventHandler, uiWorkletRuntime); -#else audioapi::AudioAPIModuleInstaller::injectJSIBindings(jsiRuntime, jsCallInvoker, _eventHandler); -#endif NSLog(@"Successfully installed JSI bindings for react-native-audio-api!"); return @YES; diff --git a/packages/react-native-audio-api/package.json b/packages/react-native-audio-api/package.json index e55341b84..cf686fd9a 100644 --- a/packages/react-native-audio-api/package.json +++ b/packages/react-native-audio-api/package.json @@ -40,7 +40,6 @@ "!common/cpp/test", "scripts/setup-rn-audio-api-web.js", "scripts/rnaa_utils.rb", - "scripts/validate-worklets-version.js", "scripts/download-prebuilt-binaries.sh", "app.plugin.js" ], @@ -98,13 +97,7 @@ }, "peerDependencies": { "react": "*", - "react-native": "*", - "react-native-worklets": ">= 0.6.0" - }, - "peerDependenciesMeta": { - "react-native-worklets": { - "optional": true - } + "react-native": "*" }, "devDependencies": { "@babel/cli": "^7.20.0", diff --git a/packages/react-native-audio-api/scripts/rnaa_utils.rb b/packages/react-native-audio-api/scripts/rnaa_utils.rb index b291ddd93..9b2d28a77 100644 --- a/packages/react-native-audio-api/scripts/rnaa_utils.rb +++ b/packages/react-native-audio-api/scripts/rnaa_utils.rb @@ -1,12 +1,3 @@ -def check_if_worklets_enabled() - validate_worklets_script = File.expand_path(File.join(__dir__, 'validate-worklets-version.js')) - unless system("node \"#{validate_worklets_script}\"") - # If the validation script fails, we assume worklets are not present or have an incompatible version - return false - end - true -end - def try_to_parse_react_native_package_json(node_modules_dir) react_native_package_json_path = File.join(node_modules_dir, 'react-native/package.json') if !File.exist?(react_native_package_json_path) @@ -17,14 +8,10 @@ def try_to_parse_react_native_package_json(node_modules_dir) def find_audio_api_config() result = { - :worklets_enabled => nil, :react_native_common_dir => nil, - :dynamic_frameworks_audio_api_dir => nil, - :dynamic_frameworks_worklets_dir => nil + :dynamic_frameworks_audio_api_dir => nil } - result[:worklets_enabled] = check_if_worklets_enabled() - react_native_node_modules_dir = File.join(File.dirname(`cd "#{Pod::Config.instance.installation_root.to_s}" && node --print "require.resolve('react-native/package.json')"`), '..') react_native_json = try_to_parse_react_native_package_json(react_native_node_modules_dir) @@ -47,12 +34,5 @@ def find_audio_api_config() react_native_audio_api_dir_relative = Pathname.new(react_native_audio_api_dir_absolute).relative_path_from(pods_root).to_s result[:dynamic_frameworks_audio_api_dir] = react_native_audio_api_dir_relative - if result[:worklets_enabled] == true - react_native_worklets_node_modules_dir = File.join(File.dirname(`cd "#{Pod::Config.instance.installation_root.to_s}" && node --print "require.resolve('react-native-worklets/package.json')"`), '..') - react_native_worklets_dir_absolute = File.join(react_native_worklets_node_modules_dir, 'react-native-worklets') - react_native_worklets_dir_relative = Pathname.new(react_native_worklets_dir_absolute).relative_path_from(pods_root).to_s - result[:dynamic_frameworks_worklets_dir] = react_native_worklets_dir_relative - end - return result end diff --git a/packages/react-native-audio-api/scripts/validate-worklets-version.js b/packages/react-native-audio-api/scripts/validate-worklets-version.js deleted file mode 100644 index 9d2b0d99b..000000000 --- a/packages/react-native-audio-api/scripts/validate-worklets-version.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -const semverGte = require('semver/functions/gte'); - -const MIN_WORKLETS_VERSION = '0.6.0'; - - -/** - * Checks if the installed version of react-native-worklets is compatible with react-native-audio-api. - * @returns {boolean} True if the version is compatible, false otherwise. - */ -function validateVersion() { - let workletsVersion; - try { - const { version } = require('react-native-worklets/package.json'); - workletsVersion = version; - } catch (e) { - return false; - } - - return semverGte(workletsVersion, MIN_WORKLETS_VERSION); -} - -if (!validateVersion()) { - console.warn( - '[RNAudioApi] Incompatible version of react-native-worklets detected. Please install a compatible version if you want to use worklet nodes in react-native-audio-api.' - ); - process.exit(1); -} diff --git a/packages/react-native-audio-api/src/Audio/controls/AudioControls.tsx b/packages/react-native-audio-api/src/Audio/controls/AudioControls.tsx index 95c9d311e..07043d25f 100644 --- a/packages/react-native-audio-api/src/Audio/controls/AudioControls.tsx +++ b/packages/react-native-audio-api/src/Audio/controls/AudioControls.tsx @@ -59,10 +59,12 @@ const AudioControls: React.FC = () => { (x: number) => { progressTrackAnim.expand(); const d = durationRef.current; - progressTrackRef.current?.measureInWindow((_left, _y, width, _h) => { - progressMetricsWidth.value = width; - setScrubTime(timeFromLocationX(x, width, d)); - }); + progressTrackRef.current?.measureInWindow( + (_left: number, _y: number, width: number, _h: number) => { + progressMetricsWidth.value = width; + setScrubTime(timeFromLocationX(x, width, d)); + } + ); }, [progressTrackAnim, progressMetricsWidth, setScrubTime, progressTrackRef] ); @@ -147,9 +149,11 @@ const AudioControls: React.FC = () => { }, [playbackState, pause, play]); const onProgressTrackLayout = useCallback(() => { - progressTrackRef.current?.measureInWindow((_left, _y, width, _h) => { - progressMetricsWidth.value = width; - }); + progressTrackRef.current?.measureInWindow( + (_left: number, _y: number, width: number, _h: number) => { + progressMetricsWidth.value = width; + } + ); }, [progressMetricsWidth, progressTrackRef]); if (!ready) { diff --git a/packages/react-native-audio-api/src/AudioAPIModule/AudioAPIModule.ts b/packages/react-native-audio-api/src/AudioAPIModule/AudioAPIModule.ts index 22e00dbf3..c2e3f2e6c 100644 --- a/packages/react-native-audio-api/src/AudioAPIModule/AudioAPIModule.ts +++ b/packages/react-native-audio-api/src/AudioAPIModule/AudioAPIModule.ts @@ -1,25 +1,8 @@ import { NativeAudioAPIModule } from '../specs'; -import type { - WorkletRuntime, - IAudioAPIModule, - IWorkletsModule, -} from './ModuleInterfaces'; import { AudioApiError } from '../errors'; -import semverGte from 'semver/functions/gte'; - -class AudioAPIModule implements IAudioAPIModule { - #workletsModule_: IWorkletsModule | null = null; - #canUseWorklets_ = false; - #workletsVersion = 'unknown'; - #workletsAvailable_ = false; - private static readonly MIN_WORKLETS_VERSION = '0.6.0'; +class AudioAPIModule { constructor() { - // Important! Verify and import worklets first - // otherwise the native module installation might crash - // if react-native-worklets is not imported before audio-api - this.#verifyWorklets(); - if (this.#verifyInstallation()) { return; } @@ -33,29 +16,6 @@ class AudioAPIModule implements IAudioAPIModule { NativeAudioAPIModule.install(); } - #verifyWorklets(): boolean { - try { - const workletsPackage = require('react-native-worklets'); - const workletsPackageJson = require('react-native-worklets/package.json'); - this.#workletsVersion = workletsPackageJson.version; - this.#workletsAvailable_ = true; - - this.#canUseWorklets_ = semverGte( - this.#workletsVersion, - AudioAPIModule.MIN_WORKLETS_VERSION - ); - - if (this.#canUseWorklets_) { - this.#workletsModule_ = workletsPackage; - } - - return this.#canUseWorklets_; - } catch { - this.#canUseWorklets_ = false; - return false; - } - } - #verifyInstallation(): boolean { return ( globalThis.createAudioContext != null && @@ -67,53 +27,6 @@ class AudioAPIModule implements IAudioAPIModule { globalThis.AudioEventEmitter != null ); } - - get workletsModule(): IWorkletsModule | null { - return this.#workletsModule_; - } - - /** - * Indicates whether react-native-worklets are installed in matching version, - * for usage with react-native-audio-api. - */ - get canUseWorklets(): boolean { - return this.#canUseWorklets_; - } - - /** Returns the installed worklets version or 'unknown'. */ - get workletsVersion(): string { - return this.#workletsVersion; - } - - /** - * Indicates whether react-native-worklets are installed, regardless of - * version support. Useful for asserting compatibility. - */ - get areWorkletsAvailable(): boolean { - return this.#workletsAvailable_; - } - - /** - * Indicates whether the installed react-native-worklets version is supported. - * Useful for asserting compatibility. - */ - get isWorkletsVersionSupported(): boolean { - // Note: if areWorkletsAvailable is true, canUseWorklets is equivalent to version check - return this.#canUseWorklets_; - } - - /** Returns the range of supported worklets versions. */ - get supportedWorkletsVersion(): string[] { - return [`>=${AudioAPIModule.MIN_WORKLETS_VERSION}`]; - } - - public createAudioRuntime(): WorkletRuntime | null { - if (!this.#canUseWorklets_) { - return null; - } - - return this.#workletsModule_!.createWorkletRuntime('AudioWorkletRuntime'); - } } export default new AudioAPIModule(); diff --git a/packages/react-native-audio-api/src/AudioAPIModule/AudioAPIModule.web.ts b/packages/react-native-audio-api/src/AudioAPIModule/AudioAPIModule.web.ts index 34e2e2044..b32d02a4d 100644 --- a/packages/react-native-audio-api/src/AudioAPIModule/AudioAPIModule.web.ts +++ b/packages/react-native-audio-api/src/AudioAPIModule/AudioAPIModule.web.ts @@ -1,18 +1,3 @@ -import type { IAudioAPIModule, IWorkletsModule } from './ModuleInterfaces'; - -const mockGetter = (value: T) => value; - -class AudioAPIModule implements IAudioAPIModule { - public supportedWorkletsVersion = []; - workletsModule = mockGetter(null as IWorkletsModule | null); - canUseWorklets = mockGetter(false); - workletsVersion = mockGetter('unknown'); - areWorkletsAvailable = mockGetter(false); - isWorkletsVersionSupported = mockGetter(false); - - createAudioRuntime() { - return null; - } -} +class AudioAPIModule {} export default new AudioAPIModule(); diff --git a/packages/react-native-audio-api/src/AudioAPIModule/ModuleInterfaces.ts b/packages/react-native-audio-api/src/AudioAPIModule/ModuleInterfaces.ts deleted file mode 100644 index 48dafbb51..000000000 --- a/packages/react-native-audio-api/src/AudioAPIModule/ModuleInterfaces.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { ShareableWorkletCallback } from '../jsi-interfaces'; - -// Hint: Copied from react-native-worklets -// Doesn't really matter what is inside, just need a unique type -export interface WorkletRuntime { - __hostObjectWorkletRuntime: never; - readonly name: string; -} - -export interface IWorkletsModule { - makeShareableCloneRecursive: ( - workletCallback: ShareableWorkletCallback - ) => ShareableWorkletCallback; - - createWorkletRuntime: (runtimeName: string) => WorkletRuntime; -} - -export interface IAudioAPIModule { - get workletsModule(): IWorkletsModule | null; - get canUseWorklets(): boolean; - get workletsVersion(): string; - get areWorkletsAvailable(): boolean; - get isWorkletsVersionSupported(): boolean; - createAudioRuntime(): WorkletRuntime | null; -} diff --git a/packages/react-native-audio-api/src/AudioAPIModule/globals.d.ts b/packages/react-native-audio-api/src/AudioAPIModule/globals.d.ts index b5503d244..655cde36b 100644 --- a/packages/react-native-audio-api/src/AudioAPIModule/globals.d.ts +++ b/packages/react-native-audio-api/src/AudioAPIModule/globals.d.ts @@ -10,17 +10,11 @@ import type { /* eslint-disable no-var */ declare global { - var createAudioContext: ( - sampleRate: number, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - audioWorkletRuntime: any - ) => IAudioContext; + var createAudioContext: (sampleRate: number) => IAudioContext; var createOfflineAudioContext: ( numberOfChannels: number, length: number, - sampleRate: number, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - audioWorkletRuntime: any + sampleRate: number ) => IOfflineAudioContext; var createAudioRecorder: () => IAudioRecorder; diff --git a/packages/react-native-audio-api/src/api.ts b/packages/react-native-audio-api/src/api.ts index a415c5cdb..ca1cc5204 100644 --- a/packages/react-native-audio-api/src/api.ts +++ b/packages/react-native-audio-api/src/api.ts @@ -26,9 +26,6 @@ export { default as PeriodicWave } from './core/PeriodicWave'; export { default as StereoPannerNode } from './core/StereoPannerNode'; export { default as StreamerNode } from './core/StreamerNode'; export { default as WaveShaperNode } from './core/WaveShaperNode'; -export { default as WorkletNode } from './core/WorkletNode'; -export { default as WorkletProcessingNode } from './core/WorkletProcessingNode'; -export { default as WorkletSourceNode } from './core/WorkletSourceNode'; export * from './errors'; export * from './system/types'; diff --git a/packages/react-native-audio-api/src/core/AudioContext.ts b/packages/react-native-audio-api/src/core/AudioContext.ts index dd3fe69f5..852d14326 100644 --- a/packages/react-native-audio-api/src/core/AudioContext.ts +++ b/packages/react-native-audio-api/src/core/AudioContext.ts @@ -1,4 +1,3 @@ -import AudioAPIModule from '../AudioAPIModule'; import { NotSupportedError } from '../errors'; import { AudioTagHandle } from '../Audio/types'; import { IAudioContext } from '../jsi-interfaces'; @@ -19,12 +18,9 @@ export default class AudioContext extends BaseAudioContext { ); } - const audioRuntime = AudioAPIModule.createAudioRuntime(); - super( globalThis.createAudioContext( - options?.sampleRate || AudioManager.getDevicePreferredSampleRate(), - audioRuntime + options?.sampleRate || AudioManager.getDevicePreferredSampleRate() ) ); } diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts index 73555006d..92a6af463 100644 --- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts +++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts @@ -4,8 +4,7 @@ import { NotSupportedError, } from '../errors'; import { IBaseAudioContext } from '../jsi-interfaces'; -import { AudioWorkletRuntime, ContextState, DecodeDataInput } from '../types'; -import { assertWorkletsEnabled } from '../utils'; +import { ContextState, DecodeDataInput } from '../types'; import AnalyserNode from './AnalyserNode'; import AudioBuffer from './AudioBuffer'; import AudioBufferQueueSourceNode from './AudioBufferQueueSourceNode'; @@ -23,9 +22,6 @@ import PeriodicWave from './PeriodicWave'; import StereoPannerNode from './StereoPannerNode'; import StreamerNode from './StreamerNode'; import WaveShaperNode from './WaveShaperNode'; -import WorkletNode from './WorkletNode'; -import WorkletProcessingNode from './WorkletProcessingNode'; -import WorkletSourceNode from './WorkletSourceNode'; export default class BaseAudioContext { readonly destination: AudioDestinationNode; @@ -67,60 +63,6 @@ export default class BaseAudioContext { ); } - createWorkletNode( - callback: (audioData: Array, channelCount: number) => void, - bufferLength: number, - inputChannelCount: number, - workletRuntime: AudioWorkletRuntime = 'AudioRuntime' - ): WorkletNode { - if (inputChannelCount < 1 || inputChannelCount > 32) { - throw new NotSupportedError( - `The number of input channels provided (${inputChannelCount}) can not be less than 1 or greater than 32` - ); - } - - if (bufferLength < 1) { - throw new NotSupportedError( - `The buffer length provided (${bufferLength}) can not be less than 1` - ); - } - - assertWorkletsEnabled(); - return new WorkletNode( - this, - workletRuntime, - callback, - bufferLength, - inputChannelCount - ); - } - - createWorkletProcessingNode( - callback: ( - inputData: Array, - outputData: Array, - framesToProcess: number, - currentTime: number - ) => void, - workletRuntime: AudioWorkletRuntime = 'AudioRuntime' - ): WorkletProcessingNode { - assertWorkletsEnabled(); - return new WorkletProcessingNode(this, workletRuntime, callback); - } - - createWorkletSourceNode( - callback: ( - audioData: Array, - framesToProcess: number, - currentTime: number, - startOffset: number - ) => void, - workletRuntime: AudioWorkletRuntime = 'AudioRuntime' - ): WorkletSourceNode { - assertWorkletsEnabled(); - return new WorkletSourceNode(this, workletRuntime, callback); - } - createOscillator(): OscillatorNode { return new OscillatorNode(this); } diff --git a/packages/react-native-audio-api/src/core/OfflineAudioContext.ts b/packages/react-native-audio-api/src/core/OfflineAudioContext.ts index 519f8588f..8ac418090 100644 --- a/packages/react-native-audio-api/src/core/OfflineAudioContext.ts +++ b/packages/react-native-audio-api/src/core/OfflineAudioContext.ts @@ -1,4 +1,3 @@ -import AudioAPIModule from '../AudioAPIModule'; import { InvalidStateError, NotSupportedError } from '../errors'; import { IOfflineAudioContext } from '../jsi-interfaces'; import { OfflineAudioContextOptions } from '../types'; @@ -17,16 +16,13 @@ export default class OfflineAudioContext extends BaseAudioContext { arg1?: number, arg2?: number ) { - const audioRuntime = AudioAPIModule.createAudioRuntime(); - if (typeof arg0 === 'object') { const { numberOfChannels, length, sampleRate } = arg0; super( globalThis.createOfflineAudioContext( numberOfChannels, length, - sampleRate, - audioRuntime + sampleRate ) ); @@ -36,9 +32,7 @@ export default class OfflineAudioContext extends BaseAudioContext { typeof arg1 === 'number' && typeof arg2 === 'number' ) { - super( - globalThis.createOfflineAudioContext(arg0, arg1, arg2, audioRuntime) - ); + super(globalThis.createOfflineAudioContext(arg0, arg1, arg2)); this.duration = arg1 / arg2; } else { throw new NotSupportedError('Invalid constructor arguments'); diff --git a/packages/react-native-audio-api/src/core/WorkletNode.ts b/packages/react-native-audio-api/src/core/WorkletNode.ts deleted file mode 100644 index 984fb42e2..000000000 --- a/packages/react-native-audio-api/src/core/WorkletNode.ts +++ /dev/null @@ -1,32 +0,0 @@ -import AudioNode from './AudioNode'; -import type BaseAudioContext from './BaseAudioContext'; -import { AudioWorkletRuntime } from '../types'; -import AudioAPIModule from '../AudioAPIModule'; - -export default class WorkletNode extends AudioNode { - constructor( - context: BaseAudioContext, - runtime: AudioWorkletRuntime, - callback: (audioData: Array, channelCount: number) => void, - bufferLength: number, - inputChannelCount: number - ) { - const shareableWorklet = - AudioAPIModule.workletsModule!.makeShareableCloneRecursive( - (audioBuffers: Array, channelCount: number) => { - 'worklet'; - const floatAudioData: Array = audioBuffers.map( - (buffer) => new Float32Array(buffer) - ); - callback(floatAudioData, channelCount); - } - ); - const node = context.context.createWorkletNode( - shareableWorklet, - runtime === 'UIRuntime', - bufferLength, - inputChannelCount - ); - super(context, node); - } -} diff --git a/packages/react-native-audio-api/src/core/WorkletProcessingNode.ts b/packages/react-native-audio-api/src/core/WorkletProcessingNode.ts deleted file mode 100644 index c27219bdd..000000000 --- a/packages/react-native-audio-api/src/core/WorkletProcessingNode.ts +++ /dev/null @@ -1,41 +0,0 @@ -import AudioNode from './AudioNode'; -import type BaseAudioContext from './BaseAudioContext'; -import { AudioWorkletRuntime } from '../types'; -import AudioAPIModule from '../AudioAPIModule'; - -export default class WorkletProcessingNode extends AudioNode { - constructor( - context: BaseAudioContext, - runtime: AudioWorkletRuntime, - callback: ( - inputData: Array, - outputData: Array, - framesToProcess: number, - currentTime: number - ) => void - ) { - const shareableWorklet = - AudioAPIModule.workletsModule!.makeShareableCloneRecursive( - ( - inputBuffers: Array, - outputBuffers: Array, - framesToProcess: number, - currentTime: number - ) => { - 'worklet'; - const inputData: Array = inputBuffers.map( - (buffer) => new Float32Array(buffer, 0, framesToProcess) - ); - const outputData: Array = outputBuffers.map( - (buffer) => new Float32Array(buffer, 0, framesToProcess) - ); - callback(inputData, outputData, framesToProcess, currentTime); - } - ); - const node = context.context.createWorkletProcessingNode( - shareableWorklet, - runtime === 'UIRuntime' - ); - super(context, node); - } -} diff --git a/packages/react-native-audio-api/src/core/WorkletSourceNode.ts b/packages/react-native-audio-api/src/core/WorkletSourceNode.ts deleted file mode 100644 index f05d52bef..000000000 --- a/packages/react-native-audio-api/src/core/WorkletSourceNode.ts +++ /dev/null @@ -1,38 +0,0 @@ -import AudioScheduledSourceNode from './AudioScheduledSourceNode'; -import type BaseAudioContext from './BaseAudioContext'; -import { AudioWorkletRuntime } from '../types'; -import AudioAPIModule from '../AudioAPIModule'; - -export default class WorkletSourceNode extends AudioScheduledSourceNode { - constructor( - context: BaseAudioContext, - runtime: AudioWorkletRuntime, - callback: ( - audioData: Array, - framesToProcess: number, - currentTime: number, - startOffset: number - ) => void - ) { - const shareableWorklet = - AudioAPIModule.workletsModule!.makeShareableCloneRecursive( - ( - audioBuffers: Array, - framesToProcess: number, - currentTime: number, - startOffset: number - ) => { - 'worklet'; - const floatAudioData: Array = audioBuffers.map( - (buffer) => new Float32Array(buffer) - ); - callback(floatAudioData, framesToProcess, currentTime, startOffset); - } - ); - const node = context.context.createWorkletSourceNode( - shareableWorklet, - runtime === 'UIRuntime' - ); - super(context, node); - } -} diff --git a/packages/react-native-audio-api/src/jsi-interfaces.ts b/packages/react-native-audio-api/src/jsi-interfaces.ts index 470fa9e72..7f52313f4 100644 --- a/packages/react-native-audio-api/src/jsi-interfaces.ts +++ b/packages/react-native-audio-api/src/jsi-interfaces.ts @@ -29,30 +29,6 @@ import type { // IMPORTANT: use only IClass, because it is a part of contract between cpp host object and js layer -export type WorkletNodeCallback = ( - audioData: Array, - channelCount: number -) => void; - -export type WorkletSourceNodeCallback = ( - audioData: Array, - framesToProcess: number, - currentTime: number, - startOffset: number -) => void; - -export type WorkletProcessingNodeCallback = ( - inputData: Array, - outputData: Array, - framesToProcess: number, - currentTime: number -) => void; - -export type ShareableWorkletCallback = - | WorkletNodeCallback - | WorkletSourceNodeCallback - | WorkletProcessingNodeCallback; - export interface IBaseAudioContext { readonly destination: IAudioDestinationNode; readonly state: ContextState; @@ -61,20 +37,6 @@ export interface IBaseAudioContext { readonly decoder: IAudioDecoder; createRecorderAdapter(): IRecorderAdapterNode; - createWorkletSourceNode( - shareableWorklet: ShareableWorkletCallback, - shouldUseUiRuntime: boolean - ): IWorkletSourceNode; - createWorkletNode( - shareableWorklet: ShareableWorkletCallback, - shouldUseUiRuntime: boolean, - bufferLength: number, - inputChannelCount: number - ): IWorkletNode; - createWorkletProcessingNode( - shareableWorklet: ShareableWorkletCallback, - shouldUseUiRuntime: boolean - ): IWorkletProcessingNode; createOscillator(oscillatorOptions: OscillatorOptions): IOscillatorNode; createConstantSource( constantSourceOptions: ConstantSourceOptions @@ -318,12 +280,6 @@ export interface IAnalyserNode extends IAudioNode { export interface IRecorderAdapterNode extends IAudioNode {} -export interface IWorkletNode extends IAudioNode {} - -export interface IWorkletSourceNode extends IAudioScheduledSourceNode {} - -export interface IWorkletProcessingNode extends IAudioNode {} - export interface IWaveShaperNode extends IAudioNode { readonly curve: Float32Array | null; oversample: OverSampleType; diff --git a/packages/react-native-audio-api/src/mock/index.ts b/packages/react-native-audio-api/src/mock/index.ts index 31d895892..e85576c35 100644 --- a/packages/react-native-audio-api/src/mock/index.ts +++ b/packages/react-native-audio-api/src/mock/index.ts @@ -3,7 +3,6 @@ import { AudioRecorderCallbackOptions, AudioRecorderFileOptions, AudioRecorderStartOptions, - AudioWorkletRuntime, BiquadFilterType, ChannelCountMode, ChannelInterpretation, @@ -483,48 +482,6 @@ class MediaElementAudioSourceNodeMock extends AudioNodeMock { } } -class WorkletNodeMock extends AudioNodeMock { - constructor( - context: BaseAudioContextMock, - _runtime: AudioWorkletRuntime, - _callback: (audioData: Array, channelCount: number) => void, - _bufferLength: number, - _inputChannelCount: number - ) { - super(context, {}); - } -} - -class WorkletProcessingNodeMock extends AudioNodeMock { - constructor( - context: BaseAudioContextMock, - _runtime: AudioWorkletRuntime, - _callback: ( - inputData: Array, - outputData: Array, - framesToProcess: number, - currentTime: number - ) => void - ) { - super(context, {}); - } -} - -class WorkletSourceNodeMock extends AudioScheduledSourceNodeMock { - constructor( - context: BaseAudioContextMock, - _runtime: AudioWorkletRuntime, - _callback: ( - audioData: Array, - framesToProcess: number, - currentTime: number, - startOffset: number - ) => void - ) { - super(context, {}); - } -} - class PeriodicWaveMock { constructor(_context: BaseAudioContextMock, _options?: PeriodicWaveOptions) {} } @@ -654,29 +611,6 @@ class BaseAudioContextMock { createStreamer(options: StreamerOptions): StreamerNodeMock { return new StreamerNodeMock(this, options); } - - createWorkletNode( - _shareableWorklet: Record, - _runOnUI: boolean, - _bufferLength: number, - _inputChannelCount: number - ): WorkletNodeMock { - return new WorkletNodeMock(this, 'AudioRuntime', noop, 0, 0); - } - - createWorkletProcessingNode( - _shareableWorklet: Record, - _runOnUI: boolean - ): WorkletProcessingNodeMock { - return new WorkletProcessingNodeMock(this, 'AudioRuntime', noop); - } - - createWorkletSourceNode( - _shareableWorklet: Record, - _runOnUI: boolean - ): WorkletSourceNodeMock { - return new WorkletSourceNodeMock(this, 'AudioRuntime', noop); - } } class AudioContextMock extends BaseAudioContextMock { @@ -1075,9 +1009,6 @@ export const RecorderAdapterNode = RecorderAdapterNodeMock; export const StereoPannerNode = StereoPannerNodeMock; export const StreamerNode = StreamerNodeMock; export const WaveShaperNode = WaveShaperNodeMock; -export const WorkletNode = WorkletNodeMock; -export const WorkletProcessingNode = WorkletProcessingNodeMock; -export const WorkletSourceNode = WorkletSourceNodeMock; export const PeriodicWave = PeriodicWaveMock; export const AudioManager = AudioManagerMock; @@ -1129,9 +1060,6 @@ export type RecorderAdapterNode = RecorderAdapterNodeMock; export type StereoPannerNode = StereoPannerNodeMock; export type StreamerNode = StreamerNodeMock; export type WaveShaperNode = WaveShaperNodeMock; -export type WorkletNode = WorkletNodeMock; -export type WorkletProcessingNode = WorkletProcessingNodeMock; -export type WorkletSourceNode = WorkletSourceNodeMock; export type PeriodicWave = PeriodicWaveMock; // Export types and enums @@ -1140,7 +1068,6 @@ export { AudioRecorderCallbackOptions, AudioRecorderFileOptions, AudioRecorderStartOptions, - AudioWorkletRuntime, BiquadFilterType, ChannelCountMode, ChannelInterpretation, @@ -1192,9 +1119,6 @@ export default { StereoPannerNode: StereoPannerNodeMock, StreamerNode: StreamerNodeMock, WaveShaperNode: WaveShaperNodeMock, - WorkletNode: WorkletNodeMock, - WorkletProcessingNode: WorkletProcessingNodeMock, - WorkletSourceNode: WorkletSourceNodeMock, PeriodicWave: PeriodicWaveMock, // Functions diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 9eba289a4..94f57e44a 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -38,8 +38,6 @@ export type BiquadFilterType = export type ContextState = 'running' | 'closed' | 'suspended'; -export type AudioWorkletRuntime = 'AudioRuntime' | 'UIRuntime'; - export type OscillatorType = | 'sine' | 'square' diff --git a/packages/react-native-audio-api/src/utils/index.ts b/packages/react-native-audio-api/src/utils/index.ts index 766b20f99..b47242519 100644 --- a/packages/react-native-audio-api/src/utils/index.ts +++ b/packages/react-native-audio-api/src/utils/index.ts @@ -1,23 +1,5 @@ -import AudioAPIModule from '../AudioAPIModule'; -import { AudioApiError } from '../errors'; - export { isFfmpegEnabled } from './flags'; -export function assertWorkletsEnabled() { - if (!AudioAPIModule.areWorkletsAvailable) { - throw new AudioApiError( - '[react-native-audio-api]: Worklets are not available. Please install react-native-worklets to use this feature.' - ); - } - - if (!AudioAPIModule.isWorkletsVersionSupported) { - throw new AudioApiError( - `[react-native-audio-api]: Worklets version ${AudioAPIModule.workletsVersion} is not supported. - Please install react-native-worklets of one of the following versions: [${AudioAPIModule.supportedWorkletsVersion.join(', ')}] to use this feature.` - ); - } -} - export function clamp(value: number, min: number, max: number): number { return Math.min(Math.max(value, min), max); } diff --git a/packages/react-native-audio-api/tests/integration.test.ts b/packages/react-native-audio-api/tests/integration.test.ts index 8909a1ea3..069e7a68d 100644 --- a/packages/react-native-audio-api/tests/integration.test.ts +++ b/packages/react-native-audio-api/tests/integration.test.ts @@ -137,56 +137,6 @@ describe('Mock Integration Tests', () => { }); }); - describe('Worklet Processing', () => { - it('should create and use worklet nodes for custom processing', () => { - const context = new MockAPI.AudioContext(); - - const processingCallback = jest.fn( - (inputData, outputData, framesToProcess) => { - // Mock audio processing logic - for (let channel = 0; channel < outputData.length; channel++) { - for (let i = 0; i < framesToProcess; i++) { - outputData[channel][i] = inputData[channel][i] * 0.5; // Simple gain - } - } - } - ); - - const workletNode = new MockAPI.WorkletProcessingNode( - context, - 'AudioRuntime', - processingCallback - ); - - expect(workletNode).toBeInstanceOf(MockAPI.WorkletProcessingNode); - expect(workletNode.context).toBe(context); - }); - - it('should create worklet source node for custom audio generation', () => { - const context = new MockAPI.AudioContext(); - - const sourceCallback = jest.fn( - (audioData, framesToProcess, currentTime) => { - // Mock audio generation logic - for (let channel = 0; channel < audioData.length; channel++) { - for (let i = 0; i < framesToProcess; i++) { - audioData[channel][i] = Math.sin(currentTime + i); // Simple sine wave - } - } - } - ); - - const workletSource = new MockAPI.WorkletSourceNode( - context, - 'UIRuntime', - sourceCallback - ); - - expect(workletSource).toBeInstanceOf(MockAPI.WorkletSourceNode); - expect(workletSource.context).toBe(context); - }); - }); - describe('Buffer Queue Management', () => { it('should manage audio buffer queue for seamless playback', () => { const context = new MockAPI.AudioContext(); diff --git a/packages/react-native-audio-api/tests/mock.test.ts b/packages/react-native-audio-api/tests/mock.test.ts index c8834bc14..3db5e835f 100644 --- a/packages/react-native-audio-api/tests/mock.test.ts +++ b/packages/react-native-audio-api/tests/mock.test.ts @@ -211,36 +211,6 @@ describe('React Native Audio API Mocks', () => { }); }); - describe('WorkletNodes', () => { - it('should create WorkletNode', () => { - const workletNode = new MockAPI.WorkletNode( - context, - 'AudioRuntime', - () => {}, - 1024, - 2 - ); - expect(workletNode).toBeInstanceOf(MockAPI.WorkletNode); - }); - - it('should create WorkletProcessingNode', () => { - const processingNode = new MockAPI.WorkletProcessingNode( - context, - 'AudioRuntime', - () => {} - ); - expect(processingNode).toBeInstanceOf(MockAPI.WorkletProcessingNode); - }); - - it('should create WorkletSourceNode', () => { - const sourceNode = new MockAPI.WorkletSourceNode( - context, - 'AudioRuntime', - () => {} - ); - expect(sourceNode).toBeInstanceOf(MockAPI.WorkletSourceNode); - }); - }); }); describe('AudioRecorder', () => { diff --git a/packages/test-app-screen/src/Test.tsx b/packages/test-app-screen/src/Test.tsx index 0a12d34cd..c00d1fc85 100644 --- a/packages/test-app-screen/src/Test.tsx +++ b/packages/test-app-screen/src/Test.tsx @@ -11,7 +11,6 @@ import { oscillatorTestWithStereoPanner, } from './OscillatorTest'; import { streamerTest } from './StreamingTest'; -import { workletTest } from './WorkletsTest'; import { recorderTest, recorderPlaybackTest } from './RecorderTest'; import { audioBufferFormatsTest, @@ -90,7 +89,7 @@ const Test: FC = () => { }; }, []); - const setupAudioContext = async () => { + const setupAudioContext = () => { if (!audioContextRef.current) { audioContextRef.current = new AudioContext({ sampleRate: SAMPLE_RATE }); } @@ -155,18 +154,6 @@ const Test: FC = () => { }, 5000); }; - const workletsTest = () => { - setIsTesting(true); - setupAudioContext(); - - setTestingInfo('Worklet test that reduces gain to 0.1'); - workletTest(audioContextRef); - setTimeout(() => { - setTestingInfo('Worklet test completed.'); - setIsTesting(false); - }, 4000); - }; - const audioBufferSourceTest = async () => { setupAudioContext(); setIsTesting(true); @@ -257,8 +244,7 @@ const Test: FC = () => { paddingTop: 200, backgroundColor: 'black', height: '100%', - }} - > + }}> {testingInfo} @@ -275,11 +261,6 @@ const Test: FC = () => { />