react-native-audio-api 0.8.3-nightly-e918179-20251003 → 0.8.3-nightly-ea268f4-20251005

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 (158) hide show
  1. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +31 -14
  2. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +74 -4
  3. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +3 -0
  4. package/common/cpp/audioapi/HostObjects/WorkletProcessingNodeHostObject.h +18 -0
  5. package/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.cpp +20 -1
  6. package/common/cpp/audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.h +3 -0
  7. package/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.cpp +19 -0
  8. package/common/cpp/audioapi/HostObjects/sources/ConstantSourceNodeHostObject.h +21 -0
  9. package/common/cpp/audioapi/HostObjects/sources/WorkletSourceNodeHostObject.h +18 -0
  10. package/common/cpp/audioapi/core/AudioContext.cpp +2 -2
  11. package/common/cpp/audioapi/core/AudioContext.h +2 -2
  12. package/common/cpp/audioapi/core/AudioParam.cpp +2 -2
  13. package/common/cpp/audioapi/core/BaseAudioContext.cpp +32 -3
  14. package/common/cpp/audioapi/core/BaseAudioContext.h +9 -4
  15. package/common/cpp/audioapi/core/OfflineAudioContext.cpp +2 -2
  16. package/common/cpp/audioapi/core/OfflineAudioContext.h +2 -2
  17. package/common/cpp/audioapi/core/effects/WorkletNode.cpp +4 -3
  18. package/common/cpp/audioapi/core/effects/WorkletNode.h +4 -3
  19. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +89 -0
  20. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +52 -0
  21. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +1 -6
  22. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +30 -0
  23. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +6 -0
  24. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +1 -6
  25. package/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp +51 -0
  26. package/common/cpp/audioapi/core/sources/ConstantSourceNode.h +26 -0
  27. package/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +82 -0
  28. package/common/cpp/audioapi/core/sources/WorkletSourceNode.h +47 -0
  29. package/common/cpp/audioapi/core/utils/AudioParamEventQueue.cpp +12 -6
  30. package/common/cpp/audioapi/core/utils/Constants.h +5 -0
  31. package/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h +7 -0
  32. package/common/cpp/audioapi/core/utils/worklets/{UiWorkletsRunner.cpp → WorkletsRunner.cpp} +2 -2
  33. package/common/cpp/audioapi/core/utils/worklets/{UiWorkletsRunner.h → WorkletsRunner.h} +2 -2
  34. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +2 -1
  35. package/common/cpp/audioapi/jsi/JsiPromise.cpp +23 -27
  36. package/common/cpp/audioapi/jsi/JsiPromise.h +10 -1
  37. package/common/cpp/audioapi/utils/ThreadPool.hpp +104 -0
  38. package/common/cpp/test/AudioParamTest.cpp +204 -0
  39. package/common/cpp/test/CMakeLists.txt +3 -0
  40. package/common/cpp/test/GainTest.cpp +2 -1
  41. package/common/cpp/test/OscillatorTest.cpp +2 -1
  42. package/ios/audioapi/ios/core/IOSAudioRecorder.h +2 -1
  43. package/lib/commonjs/api.js +27 -0
  44. package/lib/commonjs/api.js.map +1 -1
  45. package/lib/commonjs/api.web.js +8 -0
  46. package/lib/commonjs/api.web.js.map +1 -1
  47. package/lib/commonjs/core/AudioBufferBaseSourceNode.js +7 -7
  48. package/lib/commonjs/core/AudioBufferBaseSourceNode.js.map +1 -1
  49. package/lib/commonjs/core/AudioBufferQueueSourceNode.js +1 -6
  50. package/lib/commonjs/core/AudioBufferQueueSourceNode.js.map +1 -1
  51. package/lib/commonjs/core/AudioBufferSourceNode.js +15 -0
  52. package/lib/commonjs/core/AudioBufferSourceNode.js.map +1 -1
  53. package/lib/commonjs/core/AudioContext.js +10 -1
  54. package/lib/commonjs/core/AudioContext.js.map +1 -1
  55. package/lib/commonjs/core/BaseAudioContext.js +35 -2
  56. package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
  57. package/lib/commonjs/core/ConstantSourceNode.js +17 -0
  58. package/lib/commonjs/core/ConstantSourceNode.js.map +1 -0
  59. package/lib/commonjs/core/OfflineAudioContext.js +11 -2
  60. package/lib/commonjs/core/OfflineAudioContext.js.map +1 -1
  61. package/lib/commonjs/core/WorkletProcessingNode.js +11 -0
  62. package/lib/commonjs/core/WorkletProcessingNode.js.map +1 -0
  63. package/lib/commonjs/core/WorkletSourceNode.js +11 -0
  64. package/lib/commonjs/core/WorkletSourceNode.js.map +1 -0
  65. package/lib/commonjs/utils/index.js.map +1 -1
  66. package/lib/commonjs/web-core/AudioContext.js +4 -0
  67. package/lib/commonjs/web-core/AudioContext.js.map +1 -1
  68. package/lib/commonjs/web-core/ConstantSourceNode.js +17 -0
  69. package/lib/commonjs/web-core/ConstantSourceNode.js.map +1 -0
  70. package/lib/commonjs/web-core/OfflineAudioContext.js +4 -0
  71. package/lib/commonjs/web-core/OfflineAudioContext.js.map +1 -1
  72. package/lib/module/api.js +4 -1
  73. package/lib/module/api.js.map +1 -1
  74. package/lib/module/api.web.js +1 -0
  75. package/lib/module/api.web.js.map +1 -1
  76. package/lib/module/core/AudioBufferBaseSourceNode.js +7 -7
  77. package/lib/module/core/AudioBufferBaseSourceNode.js.map +1 -1
  78. package/lib/module/core/AudioBufferQueueSourceNode.js +1 -6
  79. package/lib/module/core/AudioBufferQueueSourceNode.js.map +1 -1
  80. package/lib/module/core/AudioBufferSourceNode.js +15 -0
  81. package/lib/module/core/AudioBufferSourceNode.js.map +1 -1
  82. package/lib/module/core/AudioContext.js +10 -1
  83. package/lib/module/core/AudioContext.js.map +1 -1
  84. package/lib/module/core/BaseAudioContext.js +35 -2
  85. package/lib/module/core/BaseAudioContext.js.map +1 -1
  86. package/lib/module/core/ConstantSourceNode.js +11 -0
  87. package/lib/module/core/ConstantSourceNode.js.map +1 -0
  88. package/lib/module/core/OfflineAudioContext.js +11 -2
  89. package/lib/module/core/OfflineAudioContext.js.map +1 -1
  90. package/lib/module/core/WorkletProcessingNode.js +5 -0
  91. package/lib/module/core/WorkletProcessingNode.js.map +1 -0
  92. package/lib/module/core/WorkletSourceNode.js +5 -0
  93. package/lib/module/core/WorkletSourceNode.js.map +1 -0
  94. package/lib/module/utils/index.js.map +1 -1
  95. package/lib/module/web-core/AudioContext.js +4 -0
  96. package/lib/module/web-core/AudioContext.js.map +1 -1
  97. package/lib/module/web-core/ConstantSourceNode.js +11 -0
  98. package/lib/module/web-core/ConstantSourceNode.js.map +1 -0
  99. package/lib/module/web-core/OfflineAudioContext.js +4 -0
  100. package/lib/module/web-core/OfflineAudioContext.js.map +1 -1
  101. package/lib/typescript/api.d.ts +6 -3
  102. package/lib/typescript/api.d.ts.map +1 -1
  103. package/lib/typescript/api.web.d.ts +1 -0
  104. package/lib/typescript/api.web.d.ts.map +1 -1
  105. package/lib/typescript/core/AudioBufferBaseSourceNode.d.ts +2 -2
  106. package/lib/typescript/core/AudioBufferBaseSourceNode.d.ts.map +1 -1
  107. package/lib/typescript/core/AudioBufferQueueSourceNode.d.ts +1 -1
  108. package/lib/typescript/core/AudioBufferQueueSourceNode.d.ts.map +1 -1
  109. package/lib/typescript/core/AudioBufferSourceNode.d.ts +4 -0
  110. package/lib/typescript/core/AudioBufferSourceNode.d.ts.map +1 -1
  111. package/lib/typescript/core/AudioContext.d.ts +1 -0
  112. package/lib/typescript/core/AudioContext.d.ts.map +1 -1
  113. package/lib/typescript/core/BaseAudioContext.d.ts +8 -2
  114. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  115. package/lib/typescript/core/ConstantSourceNode.d.ts +9 -0
  116. package/lib/typescript/core/ConstantSourceNode.d.ts.map +1 -0
  117. package/lib/typescript/core/OfflineAudioContext.d.ts +1 -0
  118. package/lib/typescript/core/OfflineAudioContext.d.ts.map +1 -1
  119. package/lib/typescript/core/WorkletProcessingNode.d.ts +4 -0
  120. package/lib/typescript/core/WorkletProcessingNode.d.ts.map +1 -0
  121. package/lib/typescript/core/WorkletSourceNode.d.ts +4 -0
  122. package/lib/typescript/core/WorkletSourceNode.d.ts.map +1 -0
  123. package/lib/typescript/events/types.d.ts +1 -0
  124. package/lib/typescript/events/types.d.ts.map +1 -1
  125. package/lib/typescript/interfaces.d.ts +16 -2
  126. package/lib/typescript/interfaces.d.ts.map +1 -1
  127. package/lib/typescript/types.d.ts +1 -0
  128. package/lib/typescript/types.d.ts.map +1 -1
  129. package/lib/typescript/utils/index.d.ts +1 -0
  130. package/lib/typescript/utils/index.d.ts.map +1 -1
  131. package/lib/typescript/web-core/AudioContext.d.ts +2 -0
  132. package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
  133. package/lib/typescript/web-core/BaseAudioContext.d.ts +2 -0
  134. package/lib/typescript/web-core/BaseAudioContext.d.ts.map +1 -1
  135. package/lib/typescript/web-core/ConstantSourceNode.d.ts +8 -0
  136. package/lib/typescript/web-core/ConstantSourceNode.d.ts.map +1 -0
  137. package/lib/typescript/web-core/OfflineAudioContext.d.ts +2 -0
  138. package/lib/typescript/web-core/OfflineAudioContext.d.ts.map +1 -1
  139. package/package.json +1 -1
  140. package/src/api.ts +10 -2
  141. package/src/api.web.ts +1 -0
  142. package/src/core/AudioBufferBaseSourceNode.ts +9 -9
  143. package/src/core/AudioBufferQueueSourceNode.ts +1 -9
  144. package/src/core/AudioBufferSourceNode.ts +28 -0
  145. package/src/core/AudioContext.ts +12 -1
  146. package/src/core/BaseAudioContext.ts +90 -1
  147. package/src/core/ConstantSourceNode.ts +13 -0
  148. package/src/core/OfflineAudioContext.ts +18 -2
  149. package/src/core/WorkletProcessingNode.ts +3 -0
  150. package/src/core/WorkletSourceNode.ts +3 -0
  151. package/src/events/types.ts +1 -0
  152. package/src/interfaces.ts +42 -2
  153. package/src/types.ts +2 -0
  154. package/src/utils/index.ts +3 -0
  155. package/src/web-core/AudioContext.tsx +5 -0
  156. package/src/web-core/BaseAudioContext.tsx +2 -0
  157. package/src/web-core/ConstantSourceNode.tsx +12 -0
  158. package/src/web-core/OfflineAudioContext.tsx +5 -0
@@ -0,0 +1,89 @@
1
+ #include <audioapi/core/effects/WorkletProcessingNode.h>
2
+ #include <audioapi/core/utils/Constants.h>
3
+
4
+ namespace audioapi {
5
+
6
+ WorkletProcessingNode::WorkletProcessingNode(
7
+ BaseAudioContext *context,
8
+ std::shared_ptr<worklets::SerializableWorklet> &worklet,
9
+ std::weak_ptr<worklets::WorkletRuntime> runtime)
10
+ : AudioNode(context), workletRunner_(runtime), shareableWorklet_(worklet) {
11
+ isInitialized_ = true;
12
+
13
+ // Pre-allocate buffers for max 128 frames and 2 channels (stereo)
14
+ size_t maxChannelCount = 2;
15
+ inputBuffsHandles_.resize(maxChannelCount);
16
+ outputBuffsHandles_.resize(maxChannelCount);
17
+
18
+ for (size_t i = 0; i < maxChannelCount; ++i) {
19
+ auto inputBuff = new uint8_t[RENDER_QUANTUM_SIZE * sizeof(float)];
20
+ inputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(
21
+ inputBuff, RENDER_QUANTUM_SIZE * sizeof(float));
22
+
23
+ auto outputBuff = new uint8_t[RENDER_QUANTUM_SIZE * sizeof(float)];
24
+ outputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(
25
+ outputBuff, RENDER_QUANTUM_SIZE * sizeof(float));
26
+ }
27
+ }
28
+
29
+ void WorkletProcessingNode::processNode(
30
+ const std::shared_ptr<AudioBus> &processingBus,
31
+ int framesToProcess) {
32
+ size_t channelCount = std::min(
33
+ static_cast<size_t>(2), // Fixed to stereo for now
34
+ static_cast<size_t>(processingBus->getNumberOfChannels()));
35
+
36
+ // Copy input data to pre-allocated input buffers
37
+ for (size_t ch = 0; ch < channelCount; ch++) {
38
+ auto channelData = processingBus->getChannel(ch)->getData();
39
+ std::memcpy(
40
+ /* dest */ inputBuffsHandles_[ch]->data(),
41
+ /* src */ reinterpret_cast<const uint8_t *>(channelData),
42
+ /* size */ framesToProcess * sizeof(float));
43
+ }
44
+
45
+ // Execute the worklet
46
+ auto result = workletRunner_.executeOnRuntimeGuardedSync(
47
+ [this, channelCount, framesToProcess](jsi::Runtime &rt) {
48
+ auto inputJsArray = jsi::Array(rt, channelCount);
49
+ auto outputJsArray = jsi::Array(rt, channelCount);
50
+
51
+ for (size_t ch = 0; ch < channelCount; ch++) {
52
+ // Create input array buffer
53
+ auto inputArrayBuffer = jsi::ArrayBuffer(rt, inputBuffsHandles_[ch]);
54
+ inputJsArray.setValueAtIndex(rt, ch, inputArrayBuffer);
55
+
56
+ // Create output array buffer
57
+ auto outputArrayBuffer =
58
+ jsi::ArrayBuffer(rt, outputBuffsHandles_[ch]);
59
+ outputJsArray.setValueAtIndex(rt, ch, outputArrayBuffer);
60
+ }
61
+
62
+ return workletRunner_
63
+ .executeWorklet(
64
+ shareableWorklet_,
65
+ inputJsArray,
66
+ outputJsArray,
67
+ jsi::Value(rt, static_cast<int>(framesToProcess)),
68
+ jsi::Value(rt, this->context_->getCurrentTime()))
69
+ .value_or(jsi::Value::undefined());
70
+ });
71
+
72
+ // Copy processed output data back to the processing bus or zero on failure
73
+ for (size_t ch = 0; ch < channelCount; ch++) {
74
+ auto channelData = processingBus->getChannel(ch)->getData();
75
+
76
+ if (result.has_value()) {
77
+ // Copy processed output data
78
+ std::memcpy(
79
+ /* dest */ reinterpret_cast<uint8_t *>(channelData),
80
+ /* src */ outputBuffsHandles_[ch]->data(),
81
+ /* size */ framesToProcess * sizeof(float));
82
+ } else {
83
+ // Zero the output on worklet execution failure
84
+ std::memset(channelData, 0, framesToProcess * sizeof(float));
85
+ }
86
+ }
87
+ }
88
+
89
+ } // namespace audioapi
@@ -0,0 +1,52 @@
1
+ #pragma once
2
+
3
+ #include <jsi/jsi.h>
4
+ #include <audioapi/core/utils/worklets/WorkletsRunner.h>
5
+ #include <audioapi/core/AudioNode.h>
6
+ #include <audioapi/core/BaseAudioContext.h>
7
+ #include <audioapi/utils/AudioBus.h>
8
+ #include <audioapi/utils/AudioArray.h>
9
+ #include <audioapi/jsi/AudioArrayBuffer.h>
10
+
11
+ #include <memory>
12
+ #include <vector>
13
+
14
+ namespace audioapi {
15
+
16
+ #if RN_AUDIO_API_TEST
17
+ class WorkletProcessingNode : public AudioNode {
18
+ public:
19
+ explicit WorkletProcessingNode(
20
+ BaseAudioContext *context,
21
+ std::shared_ptr<worklets::SerializableWorklet> &worklet,
22
+ std::weak_ptr<worklets::WorkletRuntime> runtime
23
+ ) : AudioNode(context) {}
24
+
25
+ protected:
26
+ void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override {}
27
+ };
28
+ #else
29
+
30
+ using namespace facebook;
31
+
32
+ class WorkletProcessingNode : public AudioNode {
33
+ public:
34
+ explicit WorkletProcessingNode(
35
+ BaseAudioContext *context,
36
+ std::shared_ptr<worklets::SerializableWorklet> &worklet,
37
+ std::weak_ptr<worklets::WorkletRuntime> runtime
38
+ );
39
+
40
+ protected:
41
+ void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
42
+
43
+ private:
44
+ WorkletsRunner workletRunner_;
45
+ std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
46
+ std::vector<std::shared_ptr<AudioArrayBuffer>> inputBuffsHandles_;
47
+ std::vector<std::shared_ptr<AudioArrayBuffer>> outputBuffsHandles_;
48
+ };
49
+
50
+ #endif // RN_AUDIO_API_TEST
51
+
52
+ } // namespace audioapi
@@ -28,12 +28,7 @@ AudioBufferBaseSourceNode::AudioBufferBaseSourceNode(
28
28
  }
29
29
 
30
30
  AudioBufferBaseSourceNode::~AudioBufferBaseSourceNode() {
31
- if (onPositionChangedCallbackId_ != 0 &&
32
- context_->audioEventHandlerRegistry_ != nullptr) {
33
- context_->audioEventHandlerRegistry_->unregisterHandler(
34
- "positionChanged", onPositionChangedCallbackId_);
35
- onPositionChangedCallbackId_ = 0;
36
- }
31
+ clearOnPositionChangedCallback();
37
32
  }
38
33
 
39
34
  std::shared_ptr<AudioParam> AudioBufferBaseSourceNode::getDetuneParam() const {
@@ -29,6 +29,8 @@ AudioBufferSourceNode::~AudioBufferSourceNode() {
29
29
 
30
30
  buffer_.reset();
31
31
  alignedBus_.reset();
32
+
33
+ clearOnLoopEndedCallback();
32
34
  }
33
35
 
34
36
  bool AudioBufferSourceNode::getLoop() const {
@@ -123,6 +125,21 @@ void AudioBufferSourceNode::disable() {
123
125
  alignedBus_.reset();
124
126
  }
125
127
 
128
+ void AudioBufferSourceNode::clearOnLoopEndedCallback() {
129
+ if (onLoopEndedCallbackId_ == 0 || context_ == nullptr ||
130
+ context_->audioEventHandlerRegistry_ == nullptr) {
131
+ return;
132
+ }
133
+
134
+ context_->audioEventHandlerRegistry_->unregisterHandler(
135
+ "loopEnded", onLoopEndedCallbackId_);
136
+ onLoopEndedCallbackId_ = 0;
137
+ }
138
+
139
+ void AudioBufferSourceNode::setOnLoopEndedCallbackId(uint64_t callbackId) {
140
+ onLoopEndedCallbackId_ = callbackId;
141
+ }
142
+
126
143
  void AudioBufferSourceNode::processNode(
127
144
  const std::shared_ptr<AudioBus> &processingBus,
128
145
  int framesToProcess) {
@@ -150,6 +167,15 @@ double AudioBufferSourceNode::getCurrentPosition() const {
150
167
  static_cast<int>(vReadIndex_), buffer_->getSampleRate());
151
168
  }
152
169
 
170
+ void AudioBufferSourceNode::sendOnLoopEndedEvent() {
171
+ auto onLoopEndedCallbackId =
172
+ onLoopEndedCallbackId_.load(std::memory_order_acquire);
173
+ if (onLoopEndedCallbackId != 0) {
174
+ context_->audioEventHandlerRegistry_->invokeHandlerWithEventBody(
175
+ "loopEnded", onLoopEndedCallbackId_, {});
176
+ }
177
+ }
178
+
153
179
  /**
154
180
  * Helper functions
155
181
  */
@@ -217,6 +243,8 @@ void AudioBufferSourceNode::processWithoutInterpolation(
217
243
  playbackState_ = PlaybackState::STOP_SCHEDULED;
218
244
  break;
219
245
  }
246
+
247
+ sendOnLoopEndedEvent();
220
248
  }
221
249
  }
222
250
 
@@ -278,6 +306,8 @@ void AudioBufferSourceNode::processWithInterpolation(
278
306
  playbackState_ = PlaybackState::STOP_SCHEDULED;
279
307
  break;
280
308
  }
309
+
310
+ sendOnLoopEndedEvent();
281
311
  }
282
312
  }
283
313
  }
@@ -34,6 +34,9 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode {
34
34
  void start(double when, double offset, double duration = -1);
35
35
  void disable() override;
36
36
 
37
+ void clearOnLoopEndedCallback();
38
+ void setOnLoopEndedCallbackId(uint64_t callbackId);
39
+
37
40
  protected:
38
41
  void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
39
42
  double getCurrentPosition() const override;
@@ -49,6 +52,9 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode {
49
52
  std::shared_ptr<AudioBuffer> buffer_;
50
53
  std::shared_ptr<AudioBus> alignedBus_;
51
54
 
55
+ std::atomic<uint64_t> onLoopEndedCallbackId_ = 0; // 0 means no callback
56
+ void sendOnLoopEndedEvent();
57
+
52
58
  void processWithoutInterpolation(
53
59
  const std::shared_ptr<AudioBus>& processingBus,
54
60
  size_t startOffset,
@@ -17,12 +17,7 @@ AudioScheduledSourceNode::AudioScheduledSourceNode(BaseAudioContext *context)
17
17
  }
18
18
 
19
19
  AudioScheduledSourceNode::~AudioScheduledSourceNode() {
20
- if (onEndedCallbackId_ != 0 &&
21
- context_->audioEventHandlerRegistry_ != nullptr) {
22
- context_->audioEventHandlerRegistry_->unregisterHandler(
23
- "ended", onEndedCallbackId_);
24
- onEndedCallbackId_ = 0;
25
- }
20
+ clearOnEndedCallback();
26
21
  }
27
22
 
28
23
  void AudioScheduledSourceNode::start(double when) {
@@ -0,0 +1,51 @@
1
+ #include <audioapi/core/BaseAudioContext.h>
2
+ #include <audioapi/core/sources/ConstantSourceNode.h>
3
+ #include <audioapi/dsp/AudioUtils.h>
4
+ #include <audioapi/utils/AudioArray.h>
5
+ #include <audioapi/utils/AudioBus.h>
6
+
7
+ namespace audioapi {
8
+ ConstantSourceNode::ConstantSourceNode(BaseAudioContext *context)
9
+ : AudioScheduledSourceNode(context) {
10
+ offsetParam_ = std::make_shared<AudioParam>(
11
+ 1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context);
12
+ isInitialized_ = true;
13
+ }
14
+
15
+ std::shared_ptr<AudioParam> ConstantSourceNode::getOffsetParam() const {
16
+ return offsetParam_;
17
+ }
18
+
19
+ void ConstantSourceNode::processNode(
20
+ const std::shared_ptr<AudioBus> &processingBus,
21
+ int framesToProcess) {
22
+ size_t startOffset = 0;
23
+ size_t offsetLength = 0;
24
+
25
+ updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength);
26
+
27
+ if (!isPlaying() && !isStopScheduled()) {
28
+ processingBus->zero();
29
+ return;
30
+ }
31
+
32
+ auto offsetBus = offsetParam_->processARateParam(
33
+ framesToProcess, context_->getCurrentTime());
34
+
35
+ auto offsetChannelData = offsetBus->getChannel(0)->getData();
36
+
37
+ for (int channel = 0; channel < processingBus->getNumberOfChannels();
38
+ ++channel) {
39
+ auto outputChannelData = processingBus->getChannel(channel)->getData();
40
+
41
+ std::copy(
42
+ offsetChannelData + startOffset,
43
+ offsetChannelData + startOffset + offsetLength,
44
+ outputChannelData + startOffset);
45
+ }
46
+
47
+ if (isStopScheduled()) {
48
+ handleStopScheduled();
49
+ }
50
+ }
51
+ } // namespace audioapi
@@ -0,0 +1,26 @@
1
+ #pragma once
2
+
3
+ #include <audioapi/core/AudioParam.h>
4
+ #include <audioapi/core/sources/AudioScheduledSourceNode.h>
5
+
6
+ #include <cmath>
7
+ #include <memory>
8
+ #include <string>
9
+
10
+ namespace audioapi {
11
+
12
+ class AudioBus;
13
+
14
+ class ConstantSourceNode : public AudioScheduledSourceNode {
15
+ public:
16
+ explicit ConstantSourceNode(BaseAudioContext *context);
17
+
18
+ [[nodiscard]] std::shared_ptr<AudioParam> getOffsetParam() const;
19
+
20
+ protected:
21
+ void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
22
+
23
+ private:
24
+ std::shared_ptr<AudioParam> offsetParam_;
25
+ };
26
+ } // namespace audioapi
@@ -0,0 +1,82 @@
1
+ #include <audioapi/core/sources/WorkletSourceNode.h>
2
+ #include <audioapi/core/utils/Constants.h>
3
+
4
+ namespace audioapi {
5
+
6
+ WorkletSourceNode::WorkletSourceNode(
7
+ BaseAudioContext *context,
8
+ std::shared_ptr<worklets::SerializableWorklet> &worklet,
9
+ std::weak_ptr<worklets::WorkletRuntime> runtime)
10
+ : AudioScheduledSourceNode(context),
11
+ workletRunner_(runtime),
12
+ shareableWorklet_(worklet) {
13
+ isInitialized_ = true;
14
+
15
+ // Prepare buffers for audio processing
16
+ size_t outputChannelCount = this->getChannelCount();
17
+ outputBuffsHandles_.resize(outputChannelCount);
18
+ for (size_t i = 0; i < outputChannelCount; ++i) {
19
+ auto buff = new uint8_t[RENDER_QUANTUM_SIZE * sizeof(float)];
20
+ outputBuffsHandles_[i] = std::make_shared<AudioArrayBuffer>(
21
+ buff, RENDER_QUANTUM_SIZE * sizeof(float));
22
+ }
23
+ }
24
+
25
+ void WorkletSourceNode::processNode(
26
+ const std::shared_ptr<AudioBus> &processingBus,
27
+ int framesToProcess) {
28
+ if (isUnscheduled() || isFinished() || !isEnabled()) {
29
+ processingBus->zero();
30
+ return;
31
+ }
32
+
33
+ size_t startOffset = 0;
34
+ size_t nonSilentFramesToProcess = framesToProcess;
35
+
36
+ updatePlaybackInfo(
37
+ processingBus, framesToProcess, startOffset, nonSilentFramesToProcess);
38
+
39
+ if (nonSilentFramesToProcess == 0) {
40
+ processingBus->zero();
41
+ return;
42
+ }
43
+
44
+ size_t outputChannelCount = processingBus->getNumberOfChannels();
45
+
46
+ auto result = workletRunner_.executeOnRuntimeGuardedSync(
47
+ [this, nonSilentFramesToProcess, startOffset](jsi::Runtime &rt) {
48
+ auto jsiArray = jsi::Array(rt, this->outputBuffsHandles_.size());
49
+ for (size_t i = 0; i < this->outputBuffsHandles_.size(); ++i) {
50
+ auto arrayBuffer = jsi::ArrayBuffer(rt, this->outputBuffsHandles_[i]);
51
+ jsiArray.setValueAtIndex(rt, i, arrayBuffer);
52
+ }
53
+ return workletRunner_
54
+ .executeWorklet(
55
+ shareableWorklet_,
56
+ jsiArray,
57
+ jsi::Value(rt, static_cast<int>(nonSilentFramesToProcess)),
58
+ jsi::Value(rt, this->context_->getCurrentTime()),
59
+ jsi::Value(rt, static_cast<int>(startOffset)))
60
+ .value_or(jsi::Value::undefined());
61
+ });
62
+
63
+ // If the worklet execution failed, zero the output
64
+ // It might happen if the runtime is not available
65
+ if (!result.has_value()) {
66
+ processingBus->zero();
67
+ return;
68
+ }
69
+
70
+ // Copy the processed data back to the AudioBus
71
+ for (size_t i = 0; i < outputChannelCount; ++i) {
72
+ float *channelData = processingBus->getChannel(i)->getData();
73
+ memcpy(
74
+ channelData + startOffset,
75
+ outputBuffsHandles_[i]->data(),
76
+ nonSilentFramesToProcess * sizeof(float));
77
+ }
78
+
79
+ handleStopScheduled();
80
+ }
81
+
82
+ } // namespace audioapi
@@ -0,0 +1,47 @@
1
+ #pragma once
2
+ #include <jsi/jsi.h>
3
+ #include <audioapi/core/BaseAudioContext.h>
4
+ #include <audioapi/utils/AudioBus.h>
5
+ #include <audioapi/core/sources/AudioScheduledSourceNode.h>
6
+ #include <audioapi/core/utils/worklets/SafeIncludes.h>
7
+ #include <audioapi/core/utils/worklets/WorkletsRunner.h>
8
+ #include <audioapi/jsi/AudioArrayBuffer.h>
9
+ #include <audioapi/utils/AudioArray.h>
10
+
11
+ #include <vector>
12
+ #include <memory>
13
+
14
+ namespace audioapi {
15
+
16
+ #if RN_AUDIO_API_TEST
17
+ class WorkletSourceNode : public AudioScheduledSourceNode {
18
+ public:
19
+ explicit WorkletSourceNode(
20
+ BaseAudioContext *context,
21
+ std::shared_ptr<worklets::SerializableWorklet> &worklet,
22
+ std::weak_ptr<worklets::WorkletRuntime> runtime
23
+ ) : AudioScheduledSourceNode(context) {}
24
+
25
+ protected:
26
+ void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override {}
27
+ };
28
+ #else
29
+
30
+ class WorkletSourceNode : public AudioScheduledSourceNode {
31
+ public:
32
+ explicit WorkletSourceNode(
33
+ BaseAudioContext *context,
34
+ std::shared_ptr<worklets::SerializableWorklet> &worklet,
35
+ std::weak_ptr<worklets::WorkletRuntime> runtime
36
+ );
37
+
38
+ protected:
39
+ void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
40
+ private:
41
+ WorkletsRunner workletRunner_;
42
+ std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
43
+ std::vector<std::shared_ptr<AudioArrayBuffer>> outputBuffsHandles_;
44
+ };
45
+ #endif // RN_AUDIO_API_TEST
46
+
47
+ } // namespace audioapi
@@ -31,12 +31,12 @@ bool AudioParamEventQueue::popFront(ParamChangeEvent &event) {
31
31
 
32
32
  void AudioParamEventQueue::cancelScheduledValues(double cancelTime) {
33
33
  while (!eventQueue_.isEmpty()) {
34
- auto &front = eventQueue_.peekBack();
35
- if (front.getEndTime() < cancelTime) {
34
+ auto &back = eventQueue_.peekBack();
35
+ if (back.getEndTime() < cancelTime) {
36
36
  break;
37
37
  }
38
- if (front.getStartTime() >= cancelTime ||
39
- front.getType() == ParamChangeEventType::SET_VALUE_CURVE) {
38
+ if (back.getStartTime() >= cancelTime ||
39
+ back.getType() == ParamChangeEventType::SET_VALUE_CURVE) {
40
40
  eventQueue_.popBack();
41
41
  }
42
42
  }
@@ -46,8 +46,8 @@ void AudioParamEventQueue::cancelAndHoldAtTime(
46
46
  double cancelTime,
47
47
  double &endTimeCache) {
48
48
  while (!eventQueue_.isEmpty()) {
49
- auto &front = eventQueue_.peekBack();
50
- if (front.getEndTime() < cancelTime || front.getStartTime() <= cancelTime) {
49
+ auto &back = eventQueue_.peekBack();
50
+ if (back.getEndTime() < cancelTime || back.getStartTime() <= cancelTime) {
51
51
  break;
52
52
  }
53
53
  eventQueue_.popBack();
@@ -59,6 +59,12 @@ void AudioParamEventQueue::cancelAndHoldAtTime(
59
59
  }
60
60
 
61
61
  auto &back = eventQueue_.peekBackMut();
62
+ back.setEndValue(back.getCalculateValue()(
63
+ back.getStartTime(),
64
+ back.getEndTime(),
65
+ back.getStartValue(),
66
+ back.getEndValue(),
67
+ cancelTime));
62
68
  back.setEndTime(std::min(cancelTime, back.getEndTime()));
63
69
  }
64
70
 
@@ -16,4 +16,9 @@ static constexpr float MOST_NEGATIVE_SINGLE_FLOAT = static_cast<float>(std::nume
16
16
  static float LOG2_MOST_POSITIVE_SINGLE_FLOAT = std::log2(MOST_POSITIVE_SINGLE_FLOAT);
17
17
  static float LOG10_MOST_POSITIVE_SINGLE_FLOAT = std::log10(MOST_POSITIVE_SINGLE_FLOAT);
18
18
  static constexpr float PI = static_cast<float>(M_PI);
19
+
20
+ // buffer sizes
21
+ static constexpr size_t PROMISE_VENDOR_THREAD_POOL_WORKER_COUNT = 4;
22
+ static constexpr size_t PROMISE_VENDOR_THREAD_POOL_LOAD_BALANCER_QUEUE_SIZE = 32;
23
+ static constexpr size_t PROMISE_VENDOR_THREAD_POOL_WORKER_QUEUE_SIZE = 32;
19
24
  } // namespace audioapi
@@ -43,3 +43,10 @@ class SerializableWorklet {
43
43
  };
44
44
  } // namespace worklets
45
45
  #endif
46
+
47
+ /// @brief Struct to hold references to different runtimes used in the AudioAPI
48
+ /// @note it is used to pass them around and avoid creating multiple instances of the same runtime
49
+ struct RuntimeRegistry {
50
+ std::weak_ptr<worklets::WorkletRuntime> uiRuntime;
51
+ std::weak_ptr<worklets::WorkletRuntime> audioRuntime;
52
+ };
@@ -1,8 +1,8 @@
1
- #include <audioapi/core/utils/worklets/UiWorkletsRunner.h>
1
+ #include <audioapi/core/utils/worklets/WorkletsRunner.h>
2
2
 
3
3
  namespace audioapi {
4
4
 
5
- UiWorkletsRunner::UiWorkletsRunner(
5
+ WorkletsRunner::WorkletsRunner(
6
6
  std::weak_ptr<worklets::WorkletRuntime> weakUiRuntime) noexcept
7
7
  : weakUiRuntime_(std::move(weakUiRuntime)) {}
8
8
 
@@ -24,9 +24,9 @@ using namespace facebook;
24
24
  * This will return a shared pointer to the extracted worklet, or throw an error if the argument is invalid.
25
25
  */
26
26
 
27
- class UiWorkletsRunner {
27
+ class WorkletsRunner {
28
28
  public:
29
- explicit UiWorkletsRunner(std::weak_ptr<worklets::WorkletRuntime> weakUiRuntime) noexcept;
29
+ explicit WorkletsRunner(std::weak_ptr<worklets::WorkletRuntime> weakUiRuntime) noexcept;
30
30
 
31
31
  /// @brief Execute a job on the UI runtime safely.
32
32
  /// @param job
@@ -53,8 +53,9 @@ class AudioEventHandlerRegistry : public IAudioEventHandlerRegistry {
53
53
  "volumeChange",
54
54
  };
55
55
 
56
- static constexpr std::array<std::string_view, 5> AUDIO_API_EVENT_NAMES = {
56
+ static constexpr std::array<std::string_view, 6> AUDIO_API_EVENT_NAMES = {
57
57
  "ended",
58
+ "loopEnded",
58
59
  "audioReady",
59
60
  "positionChanged",
60
61
  "audioError",
@@ -66,8 +66,10 @@ jsi::Value PromiseVendor::createAsyncPromise(
66
66
  &&function) {
67
67
  auto &runtime = *runtime_;
68
68
  auto callInvoker = callInvoker_;
69
+ auto threadPool = threadPool_;
69
70
  auto promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");
70
- auto promiseLambda = [callInvoker = std::move(callInvoker),
71
+ auto promiseLambda = [threadPool = std::move(threadPool),
72
+ callInvoker = std::move(callInvoker),
71
73
  function = std::move(function)](
72
74
  jsi::Runtime &runtime,
73
75
  const jsi::Value &thisValue,
@@ -78,32 +80,26 @@ jsi::Value PromiseVendor::createAsyncPromise(
78
80
  auto rejectLocal = arguments[1].asObject(runtime).asFunction(runtime);
79
81
  auto reject = std::make_shared<jsi::Function>(std::move(rejectLocal));
80
82
 
81
- /// Here we can swap later for thread pool instead of creating a new thread
82
- /// each time
83
- std::thread(
84
- [callInvoker = std::move(callInvoker),
85
- function = std::move(function),
86
- resolve = std::move(resolve),
87
- reject = std::move(reject)](jsi::Runtime &runtime) {
88
- auto result = function(runtime);
89
- if (std::holds_alternative<jsi::Value>(result)) {
90
- auto valueShared = std::make_shared<jsi::Value>(
91
- std::move(std::get<jsi::Value>(result)));
92
- callInvoker->invokeAsync(
93
- [resolve, &runtime, valueShared]() -> void {
94
- resolve->call(runtime, *valueShared);
95
- });
96
- } else {
97
- auto errorMessage = std::get<std::string>(result);
98
- callInvoker->invokeAsync(
99
- [reject, &runtime, errorMessage]() -> void {
100
- auto error = jsi::JSError(runtime, errorMessage);
101
- reject->call(runtime, error.value());
102
- });
103
- }
104
- },
105
- std::ref(runtime))
106
- .detach();
83
+ threadPool->schedule([callInvoker = std::move(callInvoker),
84
+ function = std::move(function),
85
+ resolve = std::move(resolve),
86
+ reject = std::move(reject),
87
+ &runtime]() {
88
+ auto result = function(runtime);
89
+ if (std::holds_alternative<jsi::Value>(result)) {
90
+ auto valueShared = std::make_shared<jsi::Value>(
91
+ std::move(std::get<jsi::Value>(result)));
92
+ callInvoker->invokeAsync([resolve, &runtime, valueShared]() -> void {
93
+ resolve->call(runtime, *valueShared);
94
+ });
95
+ } else {
96
+ auto errorMessage = std::get<std::string>(result);
97
+ callInvoker->invokeAsync([reject, &runtime, errorMessage]() -> void {
98
+ auto error = jsi::JSError(runtime, errorMessage);
99
+ reject->call(runtime, error.value());
100
+ });
101
+ }
102
+ });
107
103
  return jsi::Value::undefined();
108
104
  };
109
105
  auto promiseFunction = jsi::Function::createFromHostFunction(
@@ -1,5 +1,7 @@
1
1
  #pragma once
2
2
 
3
+
4
+ #include <audioapi/core/utils/Constants.h>
3
5
  #include <ReactCommon/CallInvoker.h>
4
6
  #include <jsi/jsi.h>
5
7
  #include <variant>
@@ -8,6 +10,7 @@
8
10
  #include <string>
9
11
  #include <utility>
10
12
  #include <functional>
13
+ #include <audioapi/utils/ThreadPool.hpp>
11
14
 
12
15
  namespace audioapi {
13
16
 
@@ -32,7 +35,11 @@ class Promise {
32
35
 
33
36
  class PromiseVendor {
34
37
  public:
35
- PromiseVendor(jsi::Runtime *runtime, const std::shared_ptr<react::CallInvoker> &callInvoker): runtime_(runtime), callInvoker_(callInvoker) {}
38
+ PromiseVendor(jsi::Runtime *runtime, const std::shared_ptr<react::CallInvoker> &callInvoker):
39
+ runtime_(runtime), callInvoker_(callInvoker), threadPool_(std::make_shared<ThreadPool>(
40
+ audioapi::PROMISE_VENDOR_THREAD_POOL_WORKER_COUNT,
41
+ audioapi::PROMISE_VENDOR_THREAD_POOL_LOAD_BALANCER_QUEUE_SIZE,
42
+ audioapi::PROMISE_VENDOR_THREAD_POOL_WORKER_QUEUE_SIZE)) {}
36
43
 
37
44
  jsi::Value createPromise(const std::function<void(std::shared_ptr<Promise>)> &function);
38
45
 
@@ -40,6 +47,7 @@ class PromiseVendor {
40
47
  /// @param function The function to execute asynchronously. It should return either a jsi::Value on success or a std::string error message on failure.
41
48
  /// @return The created promise.
42
49
  /// @note The function is executed on a different thread, and the promise is resolved or rejected based on the function's outcome.
50
+ /// @note IMPORTANT: This function is not thread-safe and should be called from a single thread only. (comes from underlying ThreadPool implementation)
43
51
  /// @example
44
52
  /// ```cpp
45
53
  /// auto promise = promiseVendor_->createAsyncPromise(
@@ -56,6 +64,7 @@ class PromiseVendor {
56
64
  private:
57
65
  jsi::Runtime *runtime_;
58
66
  std::shared_ptr<react::CallInvoker> callInvoker_;
67
+ std::shared_ptr<ThreadPool> threadPool_;
59
68
  };
60
69
 
61
70
  } // namespace audioapi