react-native-audio-api 0.13.1 → 1.0.0-nightly-a9a9810-20260702

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/RNAudioAPI.podspec +2 -1
  2. package/android/src/main/cpp/audioapi/CMakeLists.txt +10 -0
  3. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +139 -95
  4. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +6 -5
  5. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +2 -2
  6. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +2 -2
  7. package/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp +9 -11
  8. package/common/cpp/audioapi/HostObjects/AudioNodeHostObject.cpp +34 -13
  9. package/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h +14 -4
  10. package/common/cpp/audioapi/HostObjects/AudioParamHostObject.cpp +29 -2
  11. package/common/cpp/audioapi/HostObjects/AudioParamHostObject.h +36 -1
  12. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +67 -59
  13. package/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.cpp +2 -0
  14. package/common/cpp/audioapi/HostObjects/TypedAudioNodePtr.h +16 -0
  15. package/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.cpp +18 -31
  16. package/common/cpp/audioapi/HostObjects/analysis/AnalyserNodeHostObject.h +14 -0
  17. package/common/cpp/audioapi/HostObjects/destinations/AudioDestinationNodeHostObject.h +15 -2
  18. package/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp +22 -16
  19. package/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h +10 -0
  20. package/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.cpp +18 -12
  21. package/common/cpp/audioapi/HostObjects/effects/ConvolverNodeHostObject.h +10 -0
  22. package/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp +45 -7
  23. package/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.h +10 -1
  24. package/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.cpp +7 -3
  25. package/common/cpp/audioapi/HostObjects/effects/GainNodeHostObject.h +8 -0
  26. package/common/cpp/audioapi/HostObjects/effects/IIRFilterNodeHostObject.cpp +10 -6
  27. package/common/cpp/audioapi/HostObjects/effects/IIRFilterNodeHostObject.h +13 -0
  28. package/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.cpp +8 -3
  29. package/common/cpp/audioapi/HostObjects/effects/StereoPannerNodeHostObject.h +8 -0
  30. package/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp +31 -12
  31. package/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.h +10 -0
  32. package/common/cpp/audioapi/HostObjects/effects/WorkletNodeHostObject.h +16 -2
  33. package/common/cpp/audioapi/HostObjects/effects/WorkletProcessingNodeHostObject.h +18 -2
  34. package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +1 -2
  35. package/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.cpp +20 -17
  36. package/common/cpp/audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h +4 -1
  37. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +31 -31
  38. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +10 -0
  39. package/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp +39 -36
  40. package/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.h +11 -0
  41. package/common/cpp/audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.cpp +29 -32
  42. package/common/cpp/audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.h +11 -2
  43. package/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.cpp +23 -18
  44. package/common/cpp/audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.h +5 -1
  45. package/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.cpp +8 -3
  46. package/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.h +8 -0
  47. package/common/cpp/audioapi/HostObjects/sources/MediaElementAudioSourceNodeHostObject.h +9 -5
  48. package/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.cpp +19 -12
  49. package/common/cpp/audioapi/HostObjects/sources/OscillatorNodeHostObject.h +11 -0
  50. package/common/cpp/audioapi/HostObjects/sources/RecorderAdapterNodeHostObject.h +16 -2
  51. package/common/cpp/audioapi/HostObjects/sources/StreamerNodeHostObject.h +4 -1
  52. package/common/cpp/audioapi/HostObjects/sources/WorkletSourceNodeHostObject.h +15 -2
  53. package/common/cpp/audioapi/core/AudioContext.cpp +7 -31
  54. package/common/cpp/audioapi/core/AudioContext.h +4 -7
  55. package/common/cpp/audioapi/core/AudioNode.cpp +77 -248
  56. package/common/cpp/audioapi/core/AudioNode.h +140 -53
  57. package/common/cpp/audioapi/core/AudioParam.cpp +26 -75
  58. package/common/cpp/audioapi/core/AudioParam.h +8 -18
  59. package/common/cpp/audioapi/core/BaseAudioContext.cpp +43 -174
  60. package/common/cpp/audioapi/core/BaseAudioContext.h +51 -79
  61. package/common/cpp/audioapi/core/OfflineAudioContext.cpp +1 -6
  62. package/common/cpp/audioapi/core/OfflineAudioContext.h +1 -1
  63. package/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +4 -8
  64. package/common/cpp/audioapi/core/analysis/AnalyserNode.h +1 -3
  65. package/common/cpp/audioapi/core/destinations/AudioDestinationNode.h +6 -20
  66. package/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +35 -11
  67. package/common/cpp/audioapi/core/effects/BiquadFilterNode.h +28 -3
  68. package/common/cpp/audioapi/core/effects/ConvolverNode.cpp +66 -44
  69. package/common/cpp/audioapi/core/effects/ConvolverNode.h +11 -8
  70. package/common/cpp/audioapi/core/effects/DelayNode.cpp +21 -87
  71. package/common/cpp/audioapi/core/effects/DelayNode.h +10 -13
  72. package/common/cpp/audioapi/core/effects/GainNode.cpp +5 -11
  73. package/common/cpp/audioapi/core/effects/GainNode.h +1 -3
  74. package/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +100 -11
  75. package/common/cpp/audioapi/core/effects/IIRFilterNode.h +27 -3
  76. package/common/cpp/audioapi/core/effects/StereoPannerNode.cpp +19 -15
  77. package/common/cpp/audioapi/core/effects/StereoPannerNode.h +6 -3
  78. package/common/cpp/audioapi/core/effects/WaveShaperNode.cpp +18 -13
  79. package/common/cpp/audioapi/core/effects/WaveShaperNode.h +18 -5
  80. package/common/cpp/audioapi/core/effects/WorkletNode.cpp +5 -10
  81. package/common/cpp/audioapi/core/effects/WorkletNode.h +2 -8
  82. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +4 -10
  83. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +2 -8
  84. package/common/cpp/audioapi/core/effects/delay/DelayLine.h +63 -0
  85. package/common/cpp/audioapi/core/effects/delay/DelayReader.cpp +34 -0
  86. package/common/cpp/audioapi/core/effects/delay/DelayReader.h +27 -0
  87. package/common/cpp/audioapi/core/effects/delay/DelayRingBufferOp.h +52 -0
  88. package/common/cpp/audioapi/core/effects/delay/DelayWriter.cpp +47 -0
  89. package/common/cpp/audioapi/core/effects/delay/DelayWriter.h +36 -0
  90. package/common/cpp/audioapi/core/effects/delay/host_nodes/DelayReaderHostNode.h +18 -0
  91. package/common/cpp/audioapi/core/effects/delay/host_nodes/DelayWriterHostNode.h +20 -0
  92. package/common/cpp/audioapi/core/inputs/AudioRecorder.cpp +18 -125
  93. package/common/cpp/audioapi/core/inputs/AudioRecorder.h +10 -63
  94. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +9 -10
  95. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +1 -3
  96. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +7 -12
  97. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +9 -6
  98. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.cpp +28 -35
  99. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.h +9 -10
  100. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +12 -13
  101. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +7 -5
  102. package/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp +9 -16
  103. package/common/cpp/audioapi/core/sources/ConstantSourceNode.h +1 -3
  104. package/common/cpp/audioapi/core/sources/MediaElementAudioSourceNode.cpp +22 -25
  105. package/common/cpp/audioapi/core/sources/MediaElementAudioSourceNode.h +4 -5
  106. package/common/cpp/audioapi/core/sources/OscillatorNode.cpp +9 -15
  107. package/common/cpp/audioapi/core/sources/OscillatorNode.h +1 -3
  108. package/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +4 -7
  109. package/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +3 -3
  110. package/common/cpp/audioapi/core/sources/StreamerNode.cpp +23 -34
  111. package/common/cpp/audioapi/core/sources/StreamerNode.h +11 -10
  112. package/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +14 -20
  113. package/common/cpp/audioapi/core/sources/WorkletSourceNode.h +2 -8
  114. package/common/cpp/audioapi/core/utils/Constants.h +7 -0
  115. package/common/cpp/audioapi/core/utils/{graph/Disposer.hpp → Disposer.hpp} +5 -6
  116. package/common/cpp/audioapi/core/utils/graph/AudioGraph.cpp +210 -0
  117. package/common/cpp/audioapi/core/utils/graph/AudioGraph.h +188 -0
  118. package/common/cpp/audioapi/core/utils/graph/BridgeNode.cpp +36 -0
  119. package/common/cpp/audioapi/core/utils/graph/BridgeNode.h +52 -0
  120. package/common/cpp/audioapi/core/utils/graph/Graph.cpp +174 -0
  121. package/common/cpp/audioapi/core/utils/graph/Graph.h +202 -0
  122. package/common/cpp/audioapi/core/utils/graph/GraphObject.cpp +36 -0
  123. package/common/cpp/audioapi/core/utils/graph/GraphObject.h +98 -0
  124. package/common/cpp/audioapi/core/utils/graph/HostGraph.cpp +471 -0
  125. package/common/cpp/audioapi/core/utils/graph/HostGraph.h +162 -0
  126. package/common/cpp/audioapi/core/utils/graph/HostNode.cpp +56 -0
  127. package/common/cpp/audioapi/core/utils/graph/{HostNode.hpp → HostNode.h} +32 -49
  128. package/common/cpp/audioapi/core/utils/graph/InputPool.cpp +123 -0
  129. package/common/cpp/audioapi/core/utils/graph/{InputPool.hpp → InputPool.h} +3 -126
  130. package/common/cpp/audioapi/core/utils/graph/NodeHandle.cpp +10 -0
  131. package/common/cpp/audioapi/core/utils/graph/{NodeHandle.hpp → NodeHandle.h} +5 -6
  132. package/common/cpp/audioapi/dsp/Convolver.cpp +14 -10
  133. package/common/cpp/audioapi/dsp/WaveShaper.cpp +33 -14
  134. package/common/cpp/audioapi/dsp/WaveShaper.h +23 -4
  135. package/common/cpp/audioapi/dsp/r8brain/Resampler.hpp +11 -9
  136. package/common/cpp/audioapi/jsi/JsiPromise.h +2 -2
  137. package/common/cpp/audioapi/jsi/RuntimeLifecycleMonitor.cpp +15 -5
  138. package/common/cpp/audioapi/types/NodeOptions.h +20 -4
  139. package/common/cpp/audioapi/utils/SpscChannel.hpp +80 -0
  140. package/common/cpp/audioapi/utils/ThreadPool.hpp +29 -13
  141. package/ios/audioapi/ios/core/IOSAudioPlayer.h +2 -2
  142. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +2 -6
  143. package/ios/audioapi/ios/core/IOSAudioRecorder.h +7 -3
  144. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +180 -92
  145. package/lib/commonjs/Audio/AudioFileSourceNode.js +3 -0
  146. package/lib/commonjs/Audio/AudioFileSourceNode.js.map +1 -1
  147. package/lib/commonjs/api.js +0 -8
  148. package/lib/commonjs/api.js.map +1 -1
  149. package/lib/commonjs/core/AudioBufferBaseSourceNode.js +2 -2
  150. package/lib/commonjs/core/AudioBufferBaseSourceNode.js.map +1 -1
  151. package/lib/commonjs/core/AudioNode.js +7 -1
  152. package/lib/commonjs/core/AudioNode.js.map +1 -1
  153. package/lib/commonjs/core/AudioParam.js +4 -1
  154. package/lib/commonjs/core/AudioParam.js.map +1 -1
  155. package/lib/commonjs/core/AudioRecorder.js +7 -16
  156. package/lib/commonjs/core/AudioRecorder.js.map +1 -1
  157. package/lib/commonjs/core/BaseAudioContext.js +0 -4
  158. package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
  159. package/lib/commonjs/core/BiquadFilterNode.js +4 -4
  160. package/lib/commonjs/core/BiquadFilterNode.js.map +1 -1
  161. package/lib/commonjs/core/ConstantSourceNode.js +1 -1
  162. package/lib/commonjs/core/ConstantSourceNode.js.map +1 -1
  163. package/lib/commonjs/core/DelayNode.js +1 -1
  164. package/lib/commonjs/core/DelayNode.js.map +1 -1
  165. package/lib/commonjs/core/GainNode.js +1 -1
  166. package/lib/commonjs/core/GainNode.js.map +1 -1
  167. package/lib/commonjs/core/MediaElementAudioSourceNode.js +8 -0
  168. package/lib/commonjs/core/MediaElementAudioSourceNode.js.map +1 -1
  169. package/lib/commonjs/core/OscillatorNode.js +2 -2
  170. package/lib/commonjs/core/OscillatorNode.js.map +1 -1
  171. package/lib/commonjs/core/StereoPannerNode.js +1 -1
  172. package/lib/commonjs/core/StereoPannerNode.js.map +1 -1
  173. package/lib/commonjs/web-core/AudioParam.web.js.map +1 -1
  174. package/lib/module/Audio/AudioFileSourceNode.js +3 -0
  175. package/lib/module/Audio/AudioFileSourceNode.js.map +1 -1
  176. package/lib/module/api.js +0 -1
  177. package/lib/module/api.js.map +1 -1
  178. package/lib/module/core/AudioBufferBaseSourceNode.js +2 -2
  179. package/lib/module/core/AudioBufferBaseSourceNode.js.map +1 -1
  180. package/lib/module/core/AudioNode.js +8 -2
  181. package/lib/module/core/AudioNode.js.map +1 -1
  182. package/lib/module/core/AudioParam.js +4 -1
  183. package/lib/module/core/AudioParam.js.map +1 -1
  184. package/lib/module/core/AudioRecorder.js +7 -16
  185. package/lib/module/core/AudioRecorder.js.map +1 -1
  186. package/lib/module/core/BaseAudioContext.js +0 -4
  187. package/lib/module/core/BaseAudioContext.js.map +1 -1
  188. package/lib/module/core/BiquadFilterNode.js +4 -4
  189. package/lib/module/core/BiquadFilterNode.js.map +1 -1
  190. package/lib/module/core/ConstantSourceNode.js +1 -1
  191. package/lib/module/core/ConstantSourceNode.js.map +1 -1
  192. package/lib/module/core/DelayNode.js +1 -1
  193. package/lib/module/core/DelayNode.js.map +1 -1
  194. package/lib/module/core/GainNode.js +1 -1
  195. package/lib/module/core/GainNode.js.map +1 -1
  196. package/lib/module/core/MediaElementAudioSourceNode.js +8 -0
  197. package/lib/module/core/MediaElementAudioSourceNode.js.map +1 -1
  198. package/lib/module/core/OscillatorNode.js +2 -2
  199. package/lib/module/core/OscillatorNode.js.map +1 -1
  200. package/lib/module/core/StereoPannerNode.js +1 -1
  201. package/lib/module/core/StereoPannerNode.js.map +1 -1
  202. package/lib/module/web-core/AudioParam.web.js.map +1 -1
  203. package/lib/typescript/Audio/AudioFileSourceNode.d.ts.map +1 -1
  204. package/lib/typescript/api.d.ts +0 -1
  205. package/lib/typescript/api.d.ts.map +1 -1
  206. package/lib/typescript/core/AudioNode.d.ts.map +1 -1
  207. package/lib/typescript/core/AudioParam.d.ts +3 -1
  208. package/lib/typescript/core/AudioParam.d.ts.map +1 -1
  209. package/lib/typescript/core/AudioRecorder.d.ts +4 -11
  210. package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
  211. package/lib/typescript/core/BaseAudioContext.d.ts +0 -2
  212. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  213. package/lib/typescript/core/MediaElementAudioSourceNode.d.ts +2 -0
  214. package/lib/typescript/core/MediaElementAudioSourceNode.d.ts.map +1 -1
  215. package/lib/typescript/jsi-interfaces.d.ts +2 -2
  216. package/lib/typescript/jsi-interfaces.d.ts.map +1 -1
  217. package/lib/typescript/web-core/AudioParam.web.d.ts +1 -1
  218. package/lib/typescript/web-core/AudioParam.web.d.ts.map +1 -1
  219. package/package.json +1 -1
  220. package/src/Audio/AudioFileSourceNode.ts +3 -0
  221. package/src/api.ts +0 -1
  222. package/src/core/AudioBufferBaseSourceNode.ts +2 -2
  223. package/src/core/AudioNode.ts +12 -2
  224. package/src/core/AudioParam.ts +5 -1
  225. package/src/core/AudioRecorder.ts +9 -20
  226. package/src/core/BaseAudioContext.ts +0 -5
  227. package/src/core/BiquadFilterNode.ts +4 -4
  228. package/src/core/ConstantSourceNode.ts +1 -1
  229. package/src/core/DelayNode.ts +1 -1
  230. package/src/core/GainNode.ts +1 -1
  231. package/src/core/MediaElementAudioSourceNode.ts +13 -0
  232. package/src/core/OscillatorNode.ts +2 -2
  233. package/src/core/StereoPannerNode.ts +1 -1
  234. package/src/jsi-interfaces.ts +2 -2
  235. package/src/web-core/AudioParam.web.ts +1 -2
  236. package/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp +0 -49
  237. package/common/cpp/audioapi/core/utils/AudioDestructor.hpp +0 -76
  238. package/common/cpp/audioapi/core/utils/AudioGraphManager.cpp +0 -248
  239. package/common/cpp/audioapi/core/utils/AudioGraphManager.h +0 -207
  240. package/common/cpp/audioapi/core/utils/graph/AudioGraph.hpp +0 -360
  241. package/common/cpp/audioapi/core/utils/graph/Graph.hpp +0 -227
  242. package/common/cpp/audioapi/core/utils/graph/HostGraph.hpp +0 -331
  243. package/common/cpp/audioapi/utils/MoveOnlyFunction.hpp +0 -93
  244. package/lib/commonjs/core/RecorderAdapterNode.js +0 -22
  245. package/lib/commonjs/core/RecorderAdapterNode.js.map +0 -1
  246. package/lib/module/core/RecorderAdapterNode.js +0 -16
  247. package/lib/module/core/RecorderAdapterNode.js.map +0 -1
  248. package/lib/typescript/core/RecorderAdapterNode.d.ts +0 -11
  249. package/lib/typescript/core/RecorderAdapterNode.d.ts.map +0 -1
  250. package/src/core/RecorderAdapterNode.ts +0 -17
@@ -32,7 +32,7 @@ Pod::Spec.new do |s|
32
32
  s.source = { :git => "https://github.com/software-mansion/react-native-audio-api.git", :tag => "#{s.version}" }
33
33
 
34
34
  s.subspec "audioapi" do |ss|
35
- ss.source_files = "common/cpp/audioapi/**/*.{cpp,c,h,hpp}"
35
+ ss.source_files = "common/cpp/audioapi/**/*.{cpp,c,h,hpp}", "common/cpp/test/src/graph/AudioThreadGuard.cpp"
36
36
  ss.exclude_files = $RN_AUDIO_API_FFMPEG_DISABLED ? ["common/cpp/audioapi/libs/ffmpeg/**"] : []
37
37
  ss.header_dir = "audioapi"
38
38
  ss.header_mappings_dir = "common/cpp/audioapi"
@@ -107,6 +107,7 @@ Pod::Spec.new do |s|
107
107
  .join(' '),
108
108
  "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
109
109
  "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) HAVE_ACCELERATE=1',
110
+ "GCC_PREPROCESSOR_DEFINITIONS[config=Debug]" => '$(inherited) HAVE_ACCELERATE=1 DEBUG=1',
110
111
  'OTHER_CFLAGS' => "$(inherited) #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag} #{ffmpeg_flag} #{static_external_libs_flag}",
111
112
  }
112
113
 
@@ -44,6 +44,11 @@ set(JNI_LIBS_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs)
44
44
 
45
45
  add_library(react-native-audio-api SHARED ${ANDROID_CPP_SOURCES} ${COMMON_CPP_SOURCES})
46
46
 
47
+ # Debug-only allocation auditor used by BaseAudioContext::processGraph (mirrors iOS podspec).
48
+ target_sources(react-native-audio-api PRIVATE
49
+ $<$<CONFIG:Debug>:${COMMON_CPP_DIR}/test/src/graph/AudioThreadGuard.cpp>
50
+ )
51
+
47
52
  if (NOT RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED)
48
53
  foreach(lib IN ITEMS opus opusfile ogg vorbis vorbisenc vorbisfile crypto ssl)
49
54
  add_library(${lib} STATIC IMPORTED)
@@ -165,6 +170,11 @@ if (RN_AUDIO_API_FFMPEG_DISABLED)
165
170
  )
166
171
  endif()
167
172
 
173
+ target_compile_definitions(
174
+ react-native-audio-api
175
+ PRIVATE $<$<CONFIG:Debug>:DEBUG>
176
+ )
177
+
168
178
  if (RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED)
169
179
  target_compile_definitions(
170
180
  react-native-audio-api
@@ -47,8 +47,8 @@ AndroidAudioRecorder::~AndroidAudioRecorder() {
47
47
  if (dataCallback_ != nullptr) {
48
48
  dataCallback_->cleanup();
49
49
  }
50
- if (adapterNode_ != nullptr) {
51
- adapterNode_->adapterCleanup();
50
+ if (adapterNodeHandle_ != nullptr) {
51
+ static_cast<RecorderAdapterNode *>(adapterNodeHandle_->audioNode.get())->adapterCleanup();
52
52
  }
53
53
  // oboe could be handling stopping and closing the stream, sanity check just in case
54
54
  if (mStream_ != nullptr) {
@@ -73,7 +73,7 @@ Result<NoneType, std::string> AndroidAudioRecorder::openAudioStream() {
73
73
  ->setDirection(oboe::Direction::Input)
74
74
  ->setFormat(oboe::AudioFormat::Float)
75
75
  ->setFormatConversionAllowed(true)
76
- ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
76
+ ->setPerformanceMode(oboe::PerformanceMode::None)
77
77
  ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium)
78
78
  ->setDataCallback(shared_from_this())
79
79
  ->setErrorCallback(shared_from_this());
@@ -95,8 +95,7 @@ Result<NoneType, std::string> AndroidAudioRecorder::openAudioStream() {
95
95
  /// @brief prepares and starts the audio recording process.
96
96
  /// If audio stream is opened correctly, it will set up any output configured
97
97
  /// (file writing, callback, adapter node) and start the stream.
98
- /// Invoked asynchronously from the PromiseVendor thread pool (not the JS thread);
99
- /// all shared state is guarded by the recorder locks and atomics.
98
+ /// This method should be called from the JS thread only.
100
99
  /// NOTE: I've noticed some possibly invalid file paths being returned on Android,
101
100
  /// RN side requires their "file://" prefix, but sometimes it returned raw path.
102
101
  /// Most likely this was due to alpha version mistakes, but in case of problems leaving this here. (ㆆ _ ㆆ)
@@ -133,25 +132,20 @@ Result<NoneType, std::string> AndroidAudioRecorder::start(const std::string &fil
133
132
 
134
133
  if (wantsCallback()) {
135
134
  if (dataCallback_ == nullptr) {
136
- dataCallback_ = std::make_shared<AndroidRecorderCallback>(
137
- audioEventHandlerRegistry_,
138
- dataCallbackMetadata_.sampleRate,
139
- dataCallbackMetadata_.bufferLength,
140
- dataCallbackMetadata_.channelCount,
141
- dataCallbackMetadata_.callbackId);
142
-
143
- dataCallback_->setOnErrorCallback(errorCallbackId_.load(std::memory_order_acquire));
135
+ return Result<NoneType, std::string>::Err("Callback output is unavailable.");
144
136
  }
145
137
 
138
+ dataCallback_->setOnErrorCallback(errorCallbackId_.load(std::memory_order_acquire));
146
139
  std::static_pointer_cast<AndroidRecorderCallback>(dataCallback_)
147
140
  ->prepare(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_);
148
141
  callbackOutputConfigured_.store(true, std::memory_order_release);
149
142
  }
150
143
 
151
- if (wantsConnection() && adapterNode_ != nullptr) {
144
+ if (wantsConnection() && adapterNodeHandle_ != nullptr) {
152
145
  deinterleavingBuffer_ = std::make_shared<AudioBuffer>(
153
146
  streamMaxBufferSizeInFrames_, streamChannelCount_, streamSampleRate_);
154
- adapterNode_->init(streamMaxBufferSizeInFrames_, streamChannelCount_, streamSampleRate_);
147
+ static_cast<RecorderAdapterNode *>(adapterNodeHandle_->audioNode.get())
148
+ ->init(streamMaxBufferSizeInFrames_, streamChannelCount_, streamSampleRate_);
155
149
  connectedConfigured_.store(true, std::memory_order_release);
156
150
  }
157
151
 
@@ -167,41 +161,41 @@ Result<NoneType, std::string> AndroidAudioRecorder::start(const std::string &fil
167
161
  }
168
162
 
169
163
  /// @brief Stops the audio stream and finalizes any output (file writing, callback, adapter node).
170
- /// Invoked asynchronously from the PromiseVendor thread pool (not the JS thread);
171
- /// all shared state is guarded by the recorder locks and atomics.
164
+ /// This method should be called from the JS thread only.
172
165
  /// @returns On success, returns the file URI, size in MB and duration in seconds of the recorded file (if file output is enabled).
173
166
  /// NOTE: due to the file access nature on Android, the size might sometimes be zeroed (really long files).
174
- AudioRecorder::StopResult AndroidAudioRecorder::stop() {
167
+ Result<std::tuple<std::vector<std::string>, double, double>, std::string>
168
+ AndroidAudioRecorder::stop() {
175
169
  std::shared_ptr<AudioFileWriter> fileWriter;
176
170
  std::shared_ptr<AudioRecorderCallback> dataCallback;
177
- std::shared_ptr<RecorderAdapterNode> adapterNode;
171
+ std::shared_ptr<utils::graph::NodeHandle> adapterNodeHandle;
178
172
  std::vector<std::string> outputPaths;
179
173
 
174
+ double outputFileSize = 0.0;
175
+ double outputDuration = 0.0;
176
+ bool hadFileOutput = false;
177
+
180
178
  {
181
179
  std::scoped_lock stopLock(callbackMutex_, fileWriterMutex_, adapterNodeMutex_);
182
180
 
183
181
  if (isIdle()) {
184
- return StopResult::Err("Recorder is not in recording state.");
182
+ return Result<std::tuple<std::vector<std::string>, double, double>, std::string>::Err(
183
+ "Recorder is not in recording state.");
184
+ }
185
+
186
+ if (mStream_ == nullptr) {
187
+ return Result<std::tuple<std::vector<std::string>, double, double>, std::string>::Err(
188
+ "Audio stream is not initialized.");
185
189
  }
186
190
 
187
191
  state_.store(RecorderState::Idle, std::memory_order_release);
192
+ mStream_->requestStop();
188
193
 
189
- // Fully close the stream rather than just stopping it. A stopped-but-open
190
- // stream keeps the data/error callbacks registered, so a later device
191
- // disconnect fires onErrorAfterClose and resurrects the stream (the mic
192
- // stays hot after stop()), and the stream's shared_ptr to this recorder
193
- // keeps the recorder alive forever once JS drops it (reference cycle).
194
- // start() reopens the stream when mStream_ is null.
195
- if (mStream_ != nullptr) {
196
- mStream_->requestStop();
197
- mStream_->close();
198
- mStream_.reset();
199
- }
194
+ hadFileOutput = usesFileOutput();
200
195
 
201
- if (usesFileOutput()) {
196
+ if (hadFileOutput) {
202
197
  fileOutputConfigured_.store(false, std::memory_order_release);
203
198
  fileWriter = std::move(fileWriter_);
204
- outputPaths = buildOutputPaths(recordingSegmentPaths_, filePath_);
205
199
  }
206
200
 
207
201
  if (usesCallback()) {
@@ -211,16 +205,44 @@ AudioRecorder::StopResult AndroidAudioRecorder::stop() {
211
205
 
212
206
  if (isConnected()) {
213
207
  connectedConfigured_.store(false, std::memory_order_release);
214
- isConnected_.store(false, std::memory_order_release);
215
- deinterleavingBuffer_ = nullptr;
216
- adapterNode = std::move(adapterNode_);
208
+ adapterNodeHandle = std::move(adapterNodeHandle_);
217
209
  }
210
+ }
218
211
 
219
- recordingSegmentPaths_.clear();
220
- filePath_ = "";
212
+ for (const auto &raw : recordingSegmentPaths_) {
213
+ if (!raw.empty()) {
214
+ outputPaths.push_back(std::format("file://{}", raw));
215
+ }
216
+ }
217
+ if (hadFileOutput && outputPaths.empty() && !filePath_.empty()) {
218
+ outputPaths.push_back(std::format("file://{}", filePath_));
219
+ }
220
+
221
+ recordingSegmentPaths_.clear();
222
+ filePath_ = "";
223
+
224
+ if (fileWriter != nullptr) {
225
+ auto fileResult = fileWriter->closeFile();
226
+
227
+ if (!fileResult.is_ok()) {
228
+ return Result<std::tuple<std::vector<std::string>, double, double>, std::string>::Err(
229
+ "Failed to close file: " + fileResult.unwrap_err());
230
+ }
231
+
232
+ outputFileSize = std::get<0>(fileResult.unwrap());
233
+ outputDuration = std::get<1>(fileResult.unwrap());
234
+ }
235
+
236
+ if (dataCallback != nullptr) {
237
+ dataCallback->cleanup();
221
238
  }
222
239
 
223
- return finalizeStoppedOutputs(fileWriter, dataCallback, adapterNode, std::move(outputPaths));
240
+ if (adapterNodeHandle != nullptr) {
241
+ static_cast<RecorderAdapterNode *>(adapterNodeHandle->audioNode.get())->adapterCleanup();
242
+ }
243
+
244
+ return Result<std::tuple<std::vector<std::string>, double, double>, std::string>::Ok(
245
+ std::make_tuple(std::move(outputPaths), outputFileSize, outputDuration));
224
246
  }
225
247
 
226
248
  /// @brief Enables file output for the recorder with the specified properties.
@@ -295,7 +317,7 @@ Result<NoneType, std::string> AndroidAudioRecorder::setupFileWriter(
295
317
  fileWriter_ = createFileWriter(properties);
296
318
  }
297
319
 
298
- fileWriter_->assignOnErrorCallbackId(errorEvent().getCallbackId());
320
+ fileWriter_->setOnErrorCallback(errorCallbackId_.load(std::memory_order_acquire));
299
321
 
300
322
  auto backend = std::static_pointer_cast<AndroidFileWriterBackend>(fileWriter_);
301
323
  auto fileResult = backend->openFile(
@@ -316,12 +338,27 @@ Result<NoneType, std::string> AndroidAudioRecorder::setupFileWriter(
316
338
  return Result<NoneType, std::string>::Ok(None);
317
339
  }
318
340
 
341
+ /// @brief Disables file output for the recorder.
342
+ /// If the recorder is currently active, it will finalize and close the file immediately.
343
+ /// This method should be called from the JS thread only.
344
+ void AndroidAudioRecorder::disableFileOutput() {
345
+ std::shared_ptr<AudioFileWriter> fileWriter;
346
+ {
347
+ std::scoped_lock fileWriterLock(fileWriterMutex_);
348
+ fileOutputConfigured_.store(false, std::memory_order_release);
349
+ fileOutputEnabled_.store(false, std::memory_order_release);
350
+ fileWriter = std::move(fileWriter_);
351
+ }
352
+
353
+ if (fileWriter != nullptr) {
354
+ fileWriter->closeFile();
355
+ }
356
+ }
357
+
319
358
  /// @brief Pauses the audio recording stream.
320
359
  /// For session without active file output, this method acts same as stop().
321
360
  /// This method should be called from the JS thread only.
322
361
  void AndroidAudioRecorder::pause() {
323
- std::scoped_lock pauseLock(callbackMutex_, fileWriterMutex_, adapterNodeMutex_);
324
-
325
362
  if (!isRecording()) {
326
363
  return;
327
364
  }
@@ -333,9 +370,7 @@ void AndroidAudioRecorder::pause() {
333
370
  /// @brief Resumes the audio recording stream if it was previously paused.
334
371
  /// This method should be called from the JS thread only.
335
372
  void AndroidAudioRecorder::resume() {
336
- std::scoped_lock resumeLock(callbackMutex_, fileWriterMutex_, adapterNodeMutex_);
337
-
338
- if (!isPaused() || mStream_ == nullptr) {
373
+ if (!isPaused()) {
339
374
  return;
340
375
  }
341
376
 
@@ -357,16 +392,9 @@ Result<NoneType, std::string> AndroidAudioRecorder::setOnAudioReadyCallback(
357
392
  int channelCount,
358
393
  uint64_t callbackId) {
359
394
  std::scoped_lock callbackLock(callbackMutex_, errorCallbackMutex_);
360
-
361
- dataCallbackMetadata_ = {
362
- .sampleRate = sampleRate,
363
- .bufferLength = bufferLength,
364
- .channelCount = channelCount,
365
- .callbackId = callbackId};
366
-
367
395
  dataCallback_ = std::make_shared<AndroidRecorderCallback>(
368
396
  audioEventHandlerRegistry_, sampleRate, bufferLength, channelCount, callbackId);
369
- dataCallback_->setOnErrorCallback(errorEvent().getCallbackId());
397
+ dataCallback_->setOnErrorCallback(errorCallbackId_.load(std::memory_order_acquire));
370
398
  callbackOutputEnabled_.store(true, std::memory_order_release);
371
399
  callbackOutputConfigured_.store(false, std::memory_order_release);
372
400
 
@@ -379,29 +407,53 @@ Result<NoneType, std::string> AndroidAudioRecorder::setOnAudioReadyCallback(
379
407
  return Result<NoneType, std::string>::Ok(None);
380
408
  }
381
409
 
410
+ /// @brief Clears the audio data callback.
411
+ /// If the recorder is currently active, it will stop invoking the callback immediately.
412
+ /// This method should be called from the JS thread only.
413
+ void AndroidAudioRecorder::clearOnAudioReadyCallback() {
414
+ std::scoped_lock callbackLock(callbackMutex_);
415
+ callbackOutputConfigured_.store(false, std::memory_order_release);
416
+ callbackOutputEnabled_.store(false, std::memory_order_release);
417
+ dataCallback_ = nullptr;
418
+ }
419
+
382
420
  /// @brief Connects a RecorderAdapterNode to the recorder for audio data routing.
383
421
  /// If the recorder is already active, it will initialize the adapter node immediately.
384
422
  /// This method should be called from the JS thread only.
385
423
  /// @param node Shared pointer to the RecorderAdapterNode to connect.
386
- void AndroidAudioRecorder::connect(const std::shared_ptr<RecorderAdapterNode> &node) {
424
+ void AndroidAudioRecorder::connect(const std::shared_ptr<utils::graph::NodeHandle> &node) {
387
425
  std::scoped_lock adapterLock(adapterNodeMutex_);
388
- adapterNode_ = node;
426
+ adapterNodeHandle_ = node;
389
427
  isConnected_.store(true, std::memory_order_release);
390
428
  connectedConfigured_.store(false, std::memory_order_release);
391
429
 
392
430
  if (!isIdle()) {
393
431
  deinterleavingBuffer_ = std::make_shared<AudioBuffer>(
394
432
  streamMaxBufferSizeInFrames_, streamChannelCount_, streamSampleRate_);
395
- adapterNode_->init(streamMaxBufferSizeInFrames_, streamChannelCount_, streamSampleRate_);
433
+ static_cast<RecorderAdapterNode *>(adapterNodeHandle_->audioNode.get())
434
+ ->init(streamMaxBufferSizeInFrames_, streamChannelCount_, streamSampleRate_);
396
435
  connectedConfigured_.store(true, std::memory_order_release);
397
436
  }
398
437
  }
399
438
 
400
- /// @brief Releases the Android-specific deinterleaving scratch buffer when the
401
- /// adapter node is disconnected. Invoked from the shared AudioRecorder::disconnect()
402
- /// while holding adapterNodeMutex_.
403
- void AndroidAudioRecorder::clearAdapterScratchState() {
404
- deinterleavingBuffer_ = nullptr;
439
+ /// @brief Disconnects the currently connected RecorderAdapterNode from the recorder.
440
+ /// If the recorder is currently active, it will stop routing audio data immediately.
441
+ /// This method should be called from the JS thread only.
442
+ void AndroidAudioRecorder::disconnect() {
443
+ std::shared_ptr<utils::graph::NodeHandle> adapterNodeHandle;
444
+ bool hadConnection = false;
445
+ {
446
+ std::scoped_lock adapterLock(adapterNodeMutex_);
447
+ hadConnection = isConnected();
448
+ connectedConfigured_.store(false, std::memory_order_release);
449
+ isConnected_.store(false, std::memory_order_release);
450
+ deinterleavingBuffer_ = nullptr;
451
+ adapterNodeHandle = std::move(adapterNodeHandle_);
452
+ }
453
+
454
+ if (hadConnection && adapterNodeHandle != nullptr) {
455
+ static_cast<RecorderAdapterNode *>(adapterNodeHandle->audioNode.get())->adapterCleanup();
456
+ }
405
457
  }
406
458
 
407
459
  /// @brief onAudioReady callback that is invoked by the Oboe stream when new audio data is available.
@@ -441,12 +493,14 @@ oboe::DataCallbackResult AndroidAudioRecorder::onAudioReady(
441
493
 
442
494
  if (isConnected()) {
443
495
  if (auto adapterLock = Locker::tryLock(adapterNodeMutex_)) {
444
- auto adapterNode = adapterNode_;
496
+ auto adapterNodeHandle = adapterNodeHandle_;
445
497
  auto deinterleavingBuffer = deinterleavingBuffer_;
446
- if (!isConnected() || adapterNode == nullptr || deinterleavingBuffer == nullptr) {
498
+ if (!isConnected() || adapterNodeHandle == nullptr || deinterleavingBuffer == nullptr) {
447
499
  return oboe::DataCallbackResult::Continue;
448
500
  }
449
501
 
502
+ auto *adapterNode = static_cast<RecorderAdapterNode *>(adapterNodeHandle->audioNode.get());
503
+
450
504
  auto const data = static_cast<float *>(audioData);
451
505
  deinterleavingBuffer->deinterleaveFrom(data, numFrames);
452
506
 
@@ -461,13 +515,17 @@ oboe::DataCallbackResult AndroidAudioRecorder::onAudioReady(
461
515
 
462
516
  bool AndroidAudioRecorder::isRecording() const {
463
517
  return state_.load(std::memory_order_acquire) == RecorderState::Recording &&
464
- mStream_ != nullptr && mStream_->getState() == oboe::StreamState::Started;
518
+ mStream_->getState() == oboe::StreamState::Started;
465
519
  }
466
520
 
467
521
  bool AndroidAudioRecorder::isPaused() const {
468
522
  return state_.load(std::memory_order_acquire) == RecorderState::Paused;
469
523
  }
470
524
 
525
+ bool AndroidAudioRecorder::isIdle() const {
526
+ return state_.load(std::memory_order_acquire) == RecorderState::Idle;
527
+ }
528
+
471
529
  void AndroidAudioRecorder::cleanup() {
472
530
  state_.store(RecorderState::Idle, std::memory_order_release);
473
531
 
@@ -483,43 +541,29 @@ void AndroidAudioRecorder::cleanup() {
483
541
  /// @param oboeStream Pointer to the Oboe audio stream.
484
542
  /// @param error The oboe::Result error code.
485
543
  void AndroidAudioRecorder::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result error) {
486
- if (error != oboe::Result::ErrorDisconnected) {
487
- std::string message = "Android recorder error: " + std::string(oboe::convertToText(error));
488
- errorEvent().dispatch(StringPayload{.name = "message", .reason = std::move(message)});
489
- return;
490
- }
491
-
492
- // Only restart if the recorder is still supposed to be running. Restarting
493
- // unconditionally resurrected stopped recorders, so any audio route change
494
- // (e.g. a Bluetooth headset connecting or disconnecting) turned the mic
495
- // back on after stop().
496
- if (isIdle() || isPaused()) {
497
- return;
498
- }
544
+ if (error == oboe::Result::ErrorDisconnected) {
545
+ cleanup();
499
546
 
500
- // Serialize with start()/stop(): they mutate mStream_ under these locks on
501
- // the JS thread while this runs on oboe's (detached) error thread.
502
- std::scoped_lock restartLock(callbackMutex_, fileWriterMutex_, adapterNodeMutex_);
547
+ auto streamResult = openAudioStream();
503
548
 
504
- // The error thread is detached and can arrive late: if stop()/start() have
505
- // already replaced the stream this error belongs to, don't tear down the
506
- // healthy new stream.
507
- if (stream != mStream_.get()) {
508
- return;
509
- }
549
+ if (!streamResult.is_ok()) {
550
+ uint64_t callbackId = errorCallbackId_.load(std::memory_order_acquire);
510
551
 
511
- cleanup();
552
+ if (audioEventHandlerRegistry_ == nullptr || callbackId == 0) {
553
+ return;
554
+ }
512
555
 
513
- auto streamResult = openAudioStream();
556
+ std::string message = "Android recorder error: " + streamResult.unwrap_err();
557
+ audioEventHandlerRegistry_->dispatchEvent(
558
+ AudioEvent::RECORDER_ERROR,
559
+ callbackId,
560
+ StringPayload{.name = "message", .reason = std::move(message)});
561
+ return;
562
+ }
514
563
 
515
- if (!streamResult.is_ok()) {
516
- std::string message = "Android recorder error: " + streamResult.unwrap_err();
517
- errorEvent().dispatch(StringPayload{.name = "message", .reason = std::move(message)});
518
- return;
564
+ mStream_->requestStart();
565
+ state_.store(RecorderState::Recording, std::memory_order_release);
519
566
  }
520
-
521
- mStream_->requestStart();
522
- state_.store(RecorderState::Recording, std::memory_order_release);
523
567
  }
524
568
 
525
569
  } // namespace audioapi
@@ -27,31 +27,32 @@ class AndroidAudioRecorder : public oboe::AudioStreamCallback,
27
27
  void cleanup();
28
28
 
29
29
  Result<NoneType, std::string> start(const std::string &fileNameOverride) override;
30
- StopResult stop() override;
30
+ Result<std::tuple<std::vector<std::string>, double, double>, std::string> stop() override;
31
31
 
32
32
  Result<NoneType, std::string> enableFileOutput(
33
33
  std::shared_ptr<AudioFileProperties> properties) override;
34
+ void disableFileOutput() override;
34
35
 
35
36
  void pause() override;
36
37
  void resume() override;
37
38
  bool isRecording() const override;
38
39
  bool isPaused() const override;
40
+ bool isIdle() const override;
39
41
 
40
42
  Result<NoneType, std::string> setOnAudioReadyCallback(
41
43
  float sampleRate,
42
44
  size_t bufferLength,
43
45
  int channelCount,
44
46
  uint64_t callbackId) override;
47
+ void clearOnAudioReadyCallback() override;
45
48
 
46
- void connect(const std::shared_ptr<RecorderAdapterNode> &node) override;
49
+ void connect(const std::shared_ptr<utils::graph::NodeHandle> &node) override;
50
+ void disconnect() override;
47
51
 
48
52
  oboe::DataCallbackResult
49
53
  onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override;
50
54
  void onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result error) override;
51
55
 
52
- protected:
53
- void clearAdapterScratchState() override;
54
-
55
56
  private:
56
57
  std::shared_ptr<AudioBuffer> deinterleavingBuffer_;
57
58
 
@@ -14,7 +14,7 @@
14
14
  namespace audioapi {
15
15
 
16
16
  AudioPlayer::AudioPlayer(
17
- const std::function<void(std::shared_ptr<DSPAudioBuffer>, int)> &renderAudio,
17
+ const std::function<void(DSPAudioBuffer *, int)> &renderAudio,
18
18
  float sampleRate,
19
19
  int channelCount,
20
20
  std::mutex *driverMutex,
@@ -121,7 +121,7 @@ AudioPlayer::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numF
121
121
  auto framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE);
122
122
 
123
123
  if (isRunning_.load(std::memory_order_acquire)) {
124
- renderAudio_(buffer_, framesToProcess);
124
+ renderAudio_(buffer_.get(), framesToProcess);
125
125
  } else {
126
126
  buffer_->zero();
127
127
  }
@@ -21,7 +21,7 @@ class AudioPlayer : public AudioStreamDataCallback,
21
21
  public:
22
22
  friend class AudioContext;
23
23
  AudioPlayer(
24
- const std::function<void(std::shared_ptr<DSPAudioBuffer>, int)> &renderAudio,
24
+ const std::function<void(DSPAudioBuffer *, int)> &renderAudio,
25
25
  float sampleRate,
26
26
  int channelCount,
27
27
  std::mutex *driverMutex,
@@ -47,7 +47,7 @@ class AudioPlayer : public AudioStreamDataCallback,
47
47
  void onErrorAfterClose(AudioStream *audioStream, Result error) override;
48
48
 
49
49
  private:
50
- std::function<void(std::shared_ptr<DSPAudioBuffer>, int)> renderAudio_;
50
+ std::function<void(DSPAudioBuffer *, int)> renderAudio_;
51
51
  std::atomic<uint32_t> &currentRenders_;
52
52
  std::shared_ptr<AudioStream> mStream_;
53
53
  std::shared_ptr<DSPAudioBuffer> buffer_;
@@ -27,6 +27,7 @@ AudioContextHostObject::AudioContextHostObject(
27
27
  }
28
28
 
29
29
  JSI_HOST_FUNCTION_IMPL(AudioContextHostObject, close) {
30
+ context_->getGraph()->collectDisposedNodes();
30
31
  auto audioContext = std::static_pointer_cast<AudioContext>(context_);
31
32
  auto promise = promiseVendor_->createAsyncPromise([audioContext = std::move(audioContext)]() {
32
33
  return [audioContext](jsi::Runtime &runtime) {
@@ -39,6 +40,7 @@ JSI_HOST_FUNCTION_IMPL(AudioContextHostObject, close) {
39
40
  }
40
41
 
41
42
  JSI_HOST_FUNCTION_IMPL(AudioContextHostObject, resume) {
43
+ context_->getGraph()->collectDisposedNodes();
42
44
  auto audioContext = std::static_pointer_cast<AudioContext>(context_);
43
45
  auto promise = promiseVendor_->createAsyncPromise([audioContext = std::move(audioContext)]() {
44
46
  const auto result = audioContext->resume();
@@ -53,6 +55,7 @@ JSI_HOST_FUNCTION_IMPL(AudioContextHostObject, resume) {
53
55
  }
54
56
 
55
57
  JSI_HOST_FUNCTION_IMPL(AudioContextHostObject, suspend) {
58
+ context_->getGraph()->collectDisposedNodes();
56
59
  auto audioContext = std::static_pointer_cast<AudioContext>(context_);
57
60
  auto promise = promiseVendor_->createAsyncPromise([audioContext = std::move(audioContext)]() {
58
61
  const auto result = audioContext->suspend();
@@ -70,17 +73,12 @@ JSI_HOST_FUNCTION_IMPL(AudioContextHostObject, suspend) {
70
73
  JSI_HOST_FUNCTION_IMPL(AudioContextHostObject, createMediaElementSource) {
71
74
  auto sourceObject = args[0].asObject(runtime);
72
75
  auto fileSourceHostObject = sourceObject.getHostObject<AudioFileSourceNodeHostObject>(runtime);
73
- auto audioContext = std::static_pointer_cast<AudioContext>(context_);
74
- auto mediaElementSourceNode =
75
- audioContext->createMediaElementSource(fileSourceHostObject->getAudioFileSourceNode());
76
-
77
- if (mediaElementSourceNode == nullptr) {
78
- throw jsi::JSError(runtime, "This media element is already connected to this AudioContext.");
79
- }
80
-
81
- auto mediaElementSourceHostObject =
82
- std::make_shared<MediaElementAudioSourceNodeHostObject>(mediaElementSourceNode);
83
- return jsi::Object::createFromHostObject(runtime, mediaElementSourceHostObject);
76
+ auto *fileSourceRaw = fileSourceHostObject->audioFileSourceNode();
77
+ auto mediaElementHostObject = std::make_shared<MediaElementAudioSourceNodeHostObject>(
78
+ std::static_pointer_cast<AudioContext>(context_), fileSourceRaw);
79
+ auto object = jsi::Object::createFromHostObject(runtime, mediaElementHostObject);
80
+ object.setExternalMemoryPressure(runtime, mediaElementHostObject->getMemoryPressure());
81
+ return object;
84
82
  }
85
83
 
86
84
  } // namespace audioapi
@@ -1,16 +1,22 @@
1
1
  #include <audioapi/HostObjects/AudioNodeHostObject.h>
2
2
  #include <audioapi/HostObjects/AudioParamHostObject.h>
3
+ #include <audioapi/HostObjects/destinations/AudioDestinationNodeHostObject.h>
4
+ #include <audioapi/HostObjects/effects/DelayNodeHostObject.h>
3
5
  #include <audioapi/HostObjects/utils/JsEnumParser.h>
4
6
  #include <audioapi/core/AudioNode.h>
7
+ #include <audioapi/core/effects/delay/host_nodes/DelayReaderHostNode.h>
8
+ #include <audioapi/core/effects/delay/host_nodes/DelayWriterHostNode.h>
5
9
 
6
10
  #include <memory>
11
+ #include <utility>
7
12
 
8
13
  namespace audioapi {
9
14
 
10
15
  AudioNodeHostObject::AudioNodeHostObject(
11
- const std::shared_ptr<AudioNode> &node,
16
+ const std::shared_ptr<utils::graph::Graph> &graph,
17
+ std::unique_ptr<AudioNode> node,
12
18
  const AudioNodeOptions &options)
13
- : node_(node),
19
+ : utils::graph::HostNode(graph, std::move(node)),
14
20
  numberOfInputs_(options.numberOfInputs),
15
21
  numberOfOutputs_(options.numberOfOutputs),
16
22
  channelCount_(options.channelCount),
@@ -58,13 +64,27 @@ JSI_PROPERTY_GETTER_IMPL(AudioNodeHostObject, channelInterpretation) {
58
64
 
59
65
  JSI_HOST_FUNCTION_IMPL(AudioNodeHostObject, connect) {
60
66
  auto obj = args[0].getObject(runtime);
67
+
61
68
  if (obj.isHostObject<AudioNodeHostObject>(runtime)) {
62
- auto node = obj.getHostObject<AudioNodeHostObject>(runtime);
63
- node_->connect(std::shared_ptr<AudioNodeHostObject>(node)->node_);
64
- }
65
- if (obj.isHostObject<AudioParamHostObject>(runtime)) {
69
+ auto toNodeHost = obj.getHostObject<AudioNodeHostObject>(runtime);
70
+
71
+ // source is a delay node: route through its reader
72
+ if (auto *fromDelay = dynamic_cast<DelayNodeHostObject *>(this); fromDelay != nullptr) {
73
+ fromDelay->delayReaderHostNode_->connect(*toNodeHost);
74
+ return jsi::Value::undefined();
75
+ }
76
+
77
+ // destination is a delay node: route through its writer
78
+ if (auto *toDelay = dynamic_cast<DelayNodeHostObject *>(toNodeHost.get()); toDelay != nullptr) {
79
+ connect(*toDelay->delayWriterHostNode_);
80
+ return jsi::Value::undefined();
81
+ }
82
+
83
+ connect(*toNodeHost);
84
+ } else if (obj.isHostObject<AudioParamHostObject>(runtime)) {
66
85
  auto param = obj.getHostObject<AudioParamHostObject>(runtime);
67
- node_->connect(std::shared_ptr<AudioParamHostObject>(param)->param_);
86
+ param->connectToGraph();
87
+ graph_->addEdge(node_, param->bridgeNode());
68
88
  }
69
89
  return jsi::Value::undefined();
70
90
  }
@@ -72,19 +92,20 @@ JSI_HOST_FUNCTION_IMPL(AudioNodeHostObject, connect) {
72
92
  JSI_HOST_FUNCTION_IMPL(AudioNodeHostObject, disconnect) {
73
93
  // protect direct usage of raw jsi classes
74
94
  if (args == nullptr || args[0].isUndefined()) {
75
- node_->disconnect();
95
+ disconnect();
76
96
  return jsi::Value::undefined();
77
97
  }
98
+
78
99
  auto obj = args[0].getObject(runtime);
79
100
  if (obj.isHostObject<AudioNodeHostObject>(runtime)) {
80
101
  auto node = obj.getHostObject<AudioNodeHostObject>(runtime);
81
- node_->disconnect(std::shared_ptr<AudioNodeHostObject>(node)->node_);
82
- }
83
-
84
- if (obj.isHostObject<AudioParamHostObject>(runtime)) {
102
+ disconnect(*node);
103
+ } else if (obj.isHostObject<AudioParamHostObject>(runtime)) {
85
104
  auto param = obj.getHostObject<AudioParamHostObject>(runtime);
86
- node_->disconnect(std::shared_ptr<AudioParamHostObject>(param)->param_);
105
+ // Disconnect source → bridge
106
+ graph_->removeEdge(node_, param->bridgeNode());
87
107
  }
108
+
88
109
  return jsi::Value::undefined();
89
110
  }
90
111
  } // namespace audioapi