react-native-audio-api 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +8 -3
  2. package/RNAudioAPI.podspec +0 -2
  3. package/android/src/main/cpp/audioapi/CMakeLists.txt +8 -2
  4. package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +7 -29
  5. package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.cpp +83 -0
  6. package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.h +14 -0
  7. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +4 -3
  8. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +1 -0
  9. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +37 -21
  10. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +1 -1
  11. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +21 -0
  12. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +26 -4
  13. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +1 -0
  14. package/common/cpp/audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.cpp +2 -2
  15. package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.cpp +3 -3
  16. package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.cpp +60 -0
  17. package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.h +24 -0
  18. package/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h +2 -2
  19. package/common/cpp/audioapi/core/OfflineAudioContext.cpp +0 -1
  20. package/common/cpp/audioapi/core/OfflineAudioContext.h +0 -1
  21. package/common/cpp/audioapi/core/effects/ConvolverNode.cpp +4 -10
  22. package/common/cpp/audioapi/core/effects/ConvolverNode.h +0 -4
  23. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +13 -11
  24. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +5 -9
  25. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +47 -139
  26. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +11 -8
  27. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +29 -114
  28. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +6 -8
  29. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.cpp +9 -11
  30. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.h +1 -1
  31. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +2 -2
  32. package/common/cpp/audioapi/core/types/AudioFormat.h +3 -1
  33. package/common/cpp/audioapi/core/utils/AudioDecoder.cpp +120 -91
  34. package/common/cpp/audioapi/core/utils/AudioDecoder.h +24 -101
  35. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.cpp +862 -0
  36. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.h +164 -0
  37. package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +2 -4
  38. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +7 -12
  39. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +2 -0
  40. package/common/cpp/audioapi/core/utils/buffer/BufferProcessingDirection.h +6 -0
  41. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.cpp +110 -0
  42. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.h +75 -0
  43. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.cpp +129 -0
  44. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.h +55 -0
  45. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.cpp +95 -0
  46. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.h +52 -0
  47. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +65 -157
  48. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +52 -33
  49. package/common/cpp/audioapi/events/AudioEventPayload.h +87 -0
  50. package/common/cpp/audioapi/events/IAudioEventHandlerRegistry.h +12 -12
  51. package/common/cpp/audioapi/libs/decoding/IncrementalAudioDecoder.h +12 -10
  52. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +152 -78
  53. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +6 -6
  54. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.cpp +34 -20
  55. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.h +4 -4
  56. package/common/cpp/audioapi/utils/AudioArray.hpp +6 -1
  57. package/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +1 -1
  58. package/common/cpp/audioapi/utils/TaskOffloader.hpp +3 -5
  59. package/ios/audioapi/ios/AudioAPIModule.h +2 -1
  60. package/ios/audioapi/ios/AudioAPIModule.mm +4 -25
  61. package/ios/audioapi/ios/core/IOSAudioPlayer.h +16 -2
  62. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +90 -24
  63. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +1 -1
  64. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +64 -20
  65. package/ios/audioapi/ios/system/AudioSessionManager.mm +18 -7
  66. package/ios/audioapi/ios/system/SystemNotificationManager.mm +22 -22
  67. package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +10 -13
  68. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js +1 -1
  69. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js.map +1 -1
  70. package/lib/commonjs/api.js +8 -0
  71. package/lib/commonjs/api.js.map +1 -1
  72. package/lib/commonjs/api.web.js +5 -0
  73. package/lib/commonjs/api.web.js.map +1 -1
  74. package/lib/commonjs/core/AudioFileUtils.js +35 -0
  75. package/lib/commonjs/core/AudioFileUtils.js.map +1 -0
  76. package/lib/commonjs/mock/index.js +15 -1
  77. package/lib/commonjs/mock/index.js.map +1 -1
  78. package/lib/module/AudioAPIModule/AudioAPIModule.js +1 -1
  79. package/lib/module/AudioAPIModule/AudioAPIModule.js.map +1 -1
  80. package/lib/module/api.js +1 -0
  81. package/lib/module/api.js.map +1 -1
  82. package/lib/module/api.web.js +3 -0
  83. package/lib/module/api.web.js.map +1 -1
  84. package/lib/module/core/AudioFileUtils.js +31 -0
  85. package/lib/module/core/AudioFileUtils.js.map +1 -0
  86. package/lib/module/mock/index.js +14 -1
  87. package/lib/module/mock/index.js.map +1 -1
  88. package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts.map +1 -1
  89. package/lib/typescript/api.d.ts +1 -0
  90. package/lib/typescript/api.d.ts.map +1 -1
  91. package/lib/typescript/api.web.d.ts +1 -0
  92. package/lib/typescript/api.web.d.ts.map +1 -1
  93. package/lib/typescript/core/AudioFileUtils.d.ts +2 -0
  94. package/lib/typescript/core/AudioFileUtils.d.ts.map +1 -0
  95. package/lib/typescript/interfaces.d.ts +3 -0
  96. package/lib/typescript/interfaces.d.ts.map +1 -1
  97. package/lib/typescript/mock/index.d.ts +3 -1
  98. package/lib/typescript/mock/index.d.ts.map +1 -1
  99. package/package.json +3 -3
  100. package/scripts/download-prebuilt-binaries.sh +34 -1
  101. package/src/AudioAPIModule/AudioAPIModule.ts +1 -0
  102. package/src/AudioAPIModule/globals.d.ts +3 -0
  103. package/src/api.ts +1 -0
  104. package/src/api.web.ts +7 -0
  105. package/src/core/AudioFileUtils.ts +49 -0
  106. package/src/interfaces.ts +7 -0
  107. package/src/mock/index.ts +29 -0
@@ -0,0 +1,95 @@
1
+ #include <audioapi/core/utils/buffer/SingleBufferProcessor.h>
2
+
3
+ #include <cmath>
4
+ #include <cstddef>
5
+ #include <memory>
6
+
7
+ namespace audioapi {
8
+
9
+ CursorState SingleBufferProcessor::advance(double rate) {
10
+ const double currentPosition = position_;
11
+ const auto index = static_cast<size_t>(currentPosition);
12
+ const auto factor = static_cast<float>(currentPosition - static_cast<double>(index));
13
+
14
+ size_t nextIndex;
15
+ if (direction_ == BufferProcessingDirection::FORWARD) {
16
+ nextIndex = index + 1;
17
+ if (nextIndex >= endFrame_) {
18
+ nextIndex = loop_ ? startFrame_ : index;
19
+ }
20
+ } else { // REVERSE — interpolate toward the previous sample.
21
+ if (index > startFrame_) {
22
+ nextIndex = index - 1;
23
+ } else {
24
+ nextIndex = loop_ ? endFrame_ - 1 : index;
25
+ }
26
+ }
27
+
28
+ position_ += rate;
29
+
30
+ const bool atEnd = shouldStop() &&
31
+ (currentPosition >= static_cast<double>(endFrame_) ||
32
+ currentPosition < static_cast<double>(startFrame_));
33
+
34
+ return {.index = index, .nextIndex = nextIndex, .factor = factor, .atEndOfBuffer = atEnd};
35
+ }
36
+
37
+ size_t SingleBufferProcessor::remainingInContiguousBlock() const {
38
+ if (atBoundary()) {
39
+ return 0;
40
+ }
41
+ if (direction_ == BufferProcessingDirection::REVERSE) {
42
+ // +1 because we read down to and including startFrame_
43
+ return currentIndex() - startFrame_ + 1;
44
+ }
45
+ // Pure size_t arithmetic — no double promotion, no fractional truncation.
46
+ return endFrame_ - currentIndex();
47
+ }
48
+
49
+ void SingleBufferProcessor::consume(size_t frames) {
50
+ if (direction_ == BufferProcessingDirection::REVERSE) {
51
+ position_ -= static_cast<double>(frames);
52
+ } else {
53
+ position_ += static_cast<double>(frames);
54
+ }
55
+ }
56
+
57
+ size_t SingleBufferProcessor::currentIndex() const {
58
+ return static_cast<size_t>(std::floor(position_));
59
+ }
60
+
61
+ std::shared_ptr<const AudioBuffer> SingleBufferProcessor::getBuffer() const {
62
+ return buffer_;
63
+ }
64
+
65
+ std::shared_ptr<const AudioBuffer> SingleBufferProcessor::getNextBuffer() const {
66
+ return buffer_;
67
+ }
68
+
69
+ bool SingleBufferProcessor::atBoundary() const {
70
+ return position_ < static_cast<double>(startFrame_) ||
71
+ position_ >= static_cast<double>(endFrame_);
72
+ }
73
+
74
+ bool SingleBufferProcessor::shouldStop() const {
75
+ return !loop_;
76
+ }
77
+
78
+ void SingleBufferProcessor::handleBoundary() {
79
+ if (shouldStop()) {
80
+ return;
81
+ }
82
+
83
+ if (endFrame_ <= startFrame_) {
84
+ return;
85
+ }
86
+ const auto range = static_cast<double>(endFrame_ - startFrame_);
87
+
88
+ if (position_ >= static_cast<double>(endFrame_)) {
89
+ position_ -= range;
90
+ } else if (position_ < static_cast<double>(startFrame_)) {
91
+ position_ += range;
92
+ }
93
+ }
94
+
95
+ } // namespace audioapi
@@ -0,0 +1,52 @@
1
+ #pragma once
2
+
3
+ #include <audioapi/core/utils/buffer/BufferProcessingDirection.h>
4
+ #include <audioapi/core/utils/buffer/BufferProcessorBase.h>
5
+
6
+ #include <audioapi/utils/AudioBuffer.hpp>
7
+ #include <cstddef>
8
+ #include <memory>
9
+ #include <utility>
10
+
11
+ namespace audioapi {
12
+
13
+ class SingleBufferProcessor : public BufferProcessorBase {
14
+ public:
15
+ SingleBufferProcessor() = default;
16
+
17
+ [[nodiscard]] bool atBoundary() const override;
18
+ [[nodiscard]] bool shouldStop() const override;
19
+
20
+ void setStartFrame(size_t startFrame) {
21
+ startFrame_ = startFrame;
22
+ }
23
+
24
+ void setEndFrame(size_t endFrame) {
25
+ endFrame_ = endFrame;
26
+ }
27
+
28
+ void setLoop(bool loop) {
29
+ loop_ = loop;
30
+ }
31
+
32
+ void setBuffer(std::shared_ptr<const AudioBuffer> buffer) {
33
+ buffer_ = std::move(buffer);
34
+ }
35
+
36
+ protected:
37
+ CursorState advance(double rate) override;
38
+ void consume(size_t frames) override;
39
+ [[nodiscard]] size_t remainingInContiguousBlock() const override;
40
+ [[nodiscard]] size_t currentIndex() const override;
41
+ [[nodiscard]] std::shared_ptr<const AudioBuffer> getBuffer() const override;
42
+ [[nodiscard]] std::shared_ptr<const AudioBuffer> getNextBuffer() const override;
43
+ void handleBoundary() override;
44
+
45
+ private:
46
+ std::shared_ptr<const AudioBuffer> buffer_ = nullptr;
47
+ bool loop_ = false;
48
+ size_t startFrame_ = 0;
49
+ size_t endFrame_ = 0;
50
+ };
51
+
52
+ } // namespace audioapi
@@ -1,18 +1,46 @@
1
- #include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
2
1
  #include <audioapi/events/AudioEventHandlerRegistry.h>
3
2
  #include <cstdio>
4
3
  #include <memory>
5
- #include <string>
6
4
  #include <unordered_map>
5
+ #include <utility>
7
6
 
8
7
  namespace audioapi {
9
8
 
10
9
  AudioEventHandlerRegistry::AudioEventHandlerRegistry(
11
10
  jsi::Runtime *runtime,
12
11
  const std::shared_ptr<react::CallInvoker> &callInvoker)
13
- : IAudioEventHandlerRegistry(), callInvoker_(callInvoker), runtime_(runtime) {}
12
+ : callInvoker_(callInvoker), runtime_(runtime), isExiting_(false) {
13
+ auto [sender, receiver] = channels::spsc::channel<
14
+ DispatchEvent,
15
+ EVENT_DISPATCHER_SPSC_OVERFLOW_STRATEGY,
16
+ EVENT_DISPATCHER_SPSC_WAIT_STRATEGY>(kDispatchCapacity);
17
+ sender_ = std::move(sender);
18
+ receiver_ = std::move(receiver);
19
+
20
+ workerThread_ = std::thread([this]() {
21
+ while (!isExiting_.load(std::memory_order_acquire)) {
22
+ auto item = receiver_.receive();
23
+ if (isExiting_.load(std::memory_order_acquire)) {
24
+ break;
25
+ }
26
+
27
+ auto weak = weak_from_this();
28
+ callInvoker_->invokeAsync([weak, capturedItem = std::move(item)]() {
29
+ if (auto self = weak.lock()) {
30
+ self->handleEventOnJSThread(
31
+ capturedItem.event, capturedItem.listenerId, capturedItem.payload);
32
+ }
33
+ });
34
+ }
35
+ });
36
+ }
14
37
 
15
38
  AudioEventHandlerRegistry::~AudioEventHandlerRegistry() {
39
+ isExiting_.store(true, std::memory_order_release);
40
+ sender_.send(DispatchEvent{});
41
+ if (workerThread_.joinable()) {
42
+ workerThread_.join();
43
+ }
16
44
  eventHandlers_.clear();
17
45
  }
18
46
 
@@ -22,13 +50,10 @@ uint64_t AudioEventHandlerRegistry::registerHandler(
22
50
  auto listenerId = listenerIdCounter_.fetch_add(1, std::memory_order_relaxed);
23
51
 
24
52
  if (runtime_ == nullptr) {
25
- // If runtime is not valid, we cannot register the handler
26
53
  return 0;
27
54
  }
28
55
 
29
56
  auto weakSelf = weak_from_this();
30
-
31
- // Read/Write on eventHandlers_ map only on the JS thread
32
57
  callInvoker_->invokeAsync([weakSelf, eventName, listenerId, handler]() {
33
58
  if (auto self = weakSelf.lock()) {
34
59
  self->eventHandlers_[eventName][listenerId] = handler;
@@ -40,24 +65,19 @@ uint64_t AudioEventHandlerRegistry::registerHandler(
40
65
 
41
66
  void AudioEventHandlerRegistry::unregisterHandler(AudioEvent eventName, uint64_t listenerId) {
42
67
  if (runtime_ == nullptr) {
43
- // If runtime is not valid, we cannot unregister the handler
44
68
  return;
45
69
  }
46
70
 
47
71
  auto weakSelf = weak_from_this();
48
-
49
- // Read/Write on eventHandlers_ map only on the JS thread
50
72
  callInvoker_->invokeAsync([weakSelf, eventName, listenerId]() {
51
73
  if (auto self = weakSelf.lock()) {
52
74
  auto it = self->eventHandlers_.find(eventName);
53
-
54
75
  if (it == self->eventHandlers_.end()) {
55
76
  return;
56
77
  }
57
78
 
58
79
  auto &handlersMap = it->second;
59
80
  auto handlerIt = handlersMap.find(listenerId);
60
-
61
81
  if (handlerIt != handlersMap.end()) {
62
82
  handlersMap.erase(handlerIt);
63
83
  }
@@ -65,167 +85,55 @@ void AudioEventHandlerRegistry::unregisterHandler(AudioEvent eventName, uint64_t
65
85
  });
66
86
  }
67
87
 
68
- // todo: refactor this method to be less complex and more readable
69
- // NOLINTNEXTLINE(readability-function-cognitive-complexity)
70
- void AudioEventHandlerRegistry::invokeHandlerWithEventBody(
88
+ bool AudioEventHandlerRegistry::dispatchEvent(
71
89
  AudioEvent eventName,
72
- const std::unordered_map<std::string, EventValue> &body) {
90
+ uint64_t listenerId,
91
+ AudioEventPayload &&payload) noexcept {
73
92
  if (runtime_ == nullptr) {
74
- // If runtime is not valid, we cannot unregister the handler
75
- return;
93
+ return false;
76
94
  }
77
-
78
- auto weakSelf = weak_from_this();
79
-
80
- // Read/Write on eventHandlers_ map only on the JS thread
81
- callInvoker_->invokeAsync([weakSelf, eventName, body]() {
82
- if (auto self = weakSelf.lock()) {
83
- auto it = self->eventHandlers_.find(eventName);
84
-
85
- if (it == self->eventHandlers_.end()) {
86
- // If the event name is not registered, we can skip invoking handlers
87
- return;
88
- }
89
-
90
- auto handlersMap = it->second;
91
-
92
- for (const auto &pair : handlersMap) {
93
- auto handler = pair.second;
94
-
95
- if (!handler || !handler->isFunction(*self->runtime_)) {
96
- // If the handler is not valid, we can skip it
97
- continue;
98
- }
99
-
100
- try {
101
- jsi::Object eventObject(*self->runtime_);
102
- // handle special logic for microphone, because we pass audio buffer
103
- // which has significant size
104
- if (eventName == AudioEvent::AUDIO_READY) {
105
- auto bufferIt = body.find("buffer");
106
- if (bufferIt != body.end()) {
107
- auto bufferHostObject = std::static_pointer_cast<AudioBufferHostObject>(
108
- std::get<std::shared_ptr<jsi::HostObject>>(bufferIt->second));
109
- eventObject = self->createEventObject(body, bufferHostObject->getSizeInBytes());
110
- }
111
- } else {
112
- eventObject = self->createEventObject(body);
113
- }
114
- handler->call(*self->runtime_, eventObject);
115
- } catch (const std::exception &e) {
116
- // re-throw the exception to be handled by the caller
117
- // std::exception is safe to parse by the rn bridge
118
- throw;
119
- } catch (...) {
120
- printf("Unknown exception occurred while invoking handler for event: %d\n", eventName);
121
- }
122
- }
123
- }
124
- });
95
+ return sender_.try_send(
96
+ DispatchEvent{
97
+ .event = eventName, .listenerId = listenerId, .payload = std::move(payload)}) ==
98
+ channels::spsc::ResponseStatus::SUCCESS;
125
99
  }
126
100
 
127
- // todo: refactor this method to be less complex and more readable
128
- // NOLINTNEXTLINE(readability-function-cognitive-complexity)
129
- void AudioEventHandlerRegistry::invokeHandlerWithEventBody(
101
+ void AudioEventHandlerRegistry::handleEventOnJSThread(
130
102
  AudioEvent eventName,
131
103
  uint64_t listenerId,
132
- const std::unordered_map<std::string, EventValue> &body) {
133
- if (runtime_ == nullptr) {
134
- // If runtime is not valid, we cannot unregister the handler
104
+ const AudioEventPayload &payload) {
105
+ auto it = eventHandlers_.find(eventName);
106
+ if (it == eventHandlers_.end()) {
135
107
  return;
136
108
  }
137
109
 
138
- auto weakSelf = weak_from_this();
139
-
140
- // Read/Write on eventHandlers_ map only on the JS thread
141
- callInvoker_->invokeAsync([weakSelf, eventName, listenerId, body]() {
142
- if (auto self = weakSelf.lock()) {
143
- auto it = self->eventHandlers_.find(eventName);
144
-
145
- if (it == self->eventHandlers_.end()) {
146
- // If the event name is not registered, we can skip invoking handlers
147
- return;
148
- }
149
-
150
- auto handlerIt = it->second.find(listenerId);
151
-
152
- if (handlerIt == it->second.end()) {
153
- // If the listener ID is not registered, we can skip invoking handlers
154
- return;
155
- }
156
-
157
- // Depending on how the AudioBufferSourceNode is handled on the JS side,
158
- // it sometimes might enter race condition where the ABSN is deleted on JS
159
- // side, but it is still processed on the audio thread, leading to a crash
160
- // when f.e. `positionChanged` event is triggered.
161
-
162
- // In case of debugging this, please increment the hours spent counter
163
-
164
- // Hours spent on this: 8
165
- try {
166
- if (!handlerIt->second || !handlerIt->second->isFunction(*self->runtime_)) {
167
- // If the handler is not valid, we can skip it
168
- return;
169
- }
170
- jsi::Object eventObject(*self->runtime_);
171
- // handle special logic for microphone, because we pass audio buffer which
172
- // has significant size
173
- if (eventName == AudioEvent::AUDIO_READY) {
174
- auto bufferIt = body.find("buffer");
175
- if (bufferIt != body.end()) {
176
- auto bufferHostObject = std::static_pointer_cast<AudioBufferHostObject>(
177
- std::get<std::shared_ptr<jsi::HostObject>>(bufferIt->second));
178
- eventObject = self->createEventObject(body, bufferHostObject->getSizeInBytes());
179
- }
180
- } else {
181
- eventObject = self->createEventObject(body);
182
- }
183
- handlerIt->second->call(*self->runtime_, eventObject);
184
- } catch (const std::exception &e) {
185
- // re-throw the exception to be handled by the caller
186
- // std::exception is safe to parse by the rn bridge
187
- throw;
188
- } catch (...) {
189
- printf("Unknown exception occurred while invoking handler for event: %d\n", eventName);
190
- }
110
+ if (listenerId == kBroadcastListenerId) {
111
+ auto handlersCopy = it->second;
112
+ for (const auto &pair : handlersCopy) {
113
+ invokeHandler(pair.second, payload);
191
114
  }
192
- });
193
- }
194
-
195
- jsi::Object AudioEventHandlerRegistry::createEventObject(
196
- const std::unordered_map<std::string, EventValue> &body) {
197
- auto eventObject = jsi::Object(*runtime_);
198
-
199
- for (const auto &pair : body) {
200
- const auto *name = pair.first.data();
201
- const auto &value = pair.second;
202
-
203
- if (std::holds_alternative<int>(value)) {
204
- eventObject.setProperty(*runtime_, name, std::get<int>(value));
205
- } else if (std::holds_alternative<double>(value)) {
206
- eventObject.setProperty(*runtime_, name, std::get<double>(value));
207
- } else if (std::holds_alternative<float>(value)) {
208
- eventObject.setProperty(*runtime_, name, std::get<float>(value));
209
- } else if (std::holds_alternative<bool>(value)) {
210
- eventObject.setProperty(*runtime_, name, std::get<bool>(value));
211
- } else if (std::holds_alternative<std::string>(value)) {
212
- eventObject.setProperty(*runtime_, name, std::get<std::string>(value));
213
- } else if (std::holds_alternative<std::shared_ptr<jsi::HostObject>>(value)) {
214
- auto hostObject = jsi::Object::createFromHostObject(
215
- *runtime_, std::get<std::shared_ptr<jsi::HostObject>>(value));
216
- eventObject.setProperty(*runtime_, name, hostObject);
115
+ } else {
116
+ auto handlerIt = it->second.find(listenerId);
117
+ if (handlerIt != it->second.end()) {
118
+ invokeHandler(handlerIt->second, payload);
217
119
  }
218
120
  }
219
-
220
- return eventObject;
221
121
  }
222
122
 
223
- jsi::Object AudioEventHandlerRegistry::createEventObject(
224
- const std::unordered_map<std::string, EventValue> &body,
225
- size_t memoryPressure) {
226
- auto eventObject = createEventObject(body);
227
- eventObject.setExternalMemoryPressure(*runtime_, memoryPressure);
228
- return eventObject;
123
+ void AudioEventHandlerRegistry::invokeHandler(
124
+ const std::shared_ptr<jsi::Function> &handler,
125
+ const AudioEventPayload &payload) {
126
+ if (!handler || !handler->isFunction(*runtime_)) {
127
+ return;
128
+ }
129
+ try {
130
+ auto eventObject = buildJsiObject(payload);
131
+ handler->call(*runtime_, eventObject);
132
+ } catch (const std::exception &) {
133
+ throw;
134
+ } catch (...) {
135
+ printf("Unknown exception occurred while invoking audio event handler\n");
136
+ }
229
137
  }
230
138
 
231
139
  } // namespace audioapi
@@ -2,24 +2,29 @@
2
2
 
3
3
  #include <ReactCommon/CallInvoker.h>
4
4
  #include <audioapi/events/AudioEvent.h>
5
+ #include <audioapi/events/AudioEventPayload.h>
5
6
  #include <audioapi/events/IAudioEventHandlerRegistry.h>
7
+ #include <audioapi/utils/Macros.h>
8
+ #include <audioapi/utils/SpscChannel.hpp>
6
9
  #include <jsi/jsi.h>
7
10
  #include <atomic>
8
11
  #include <memory>
9
- #include <string>
12
+ #include <thread>
10
13
  #include <unordered_map>
11
- #include <variant>
14
+
15
+ inline constexpr auto EVENT_DISPATCHER_SPSC_OVERFLOW_STRATEGY =
16
+ audioapi::channels::spsc::OverflowStrategy::WAIT_ON_FULL;
17
+ inline constexpr auto EVENT_DISPATCHER_SPSC_WAIT_STRATEGY =
18
+ audioapi::channels::spsc::WaitStrategy::ATOMIC_WAIT;
12
19
 
13
20
  namespace audioapi {
14
21
  using namespace facebook;
15
22
 
16
- using EventValue =
17
- std::variant<int, float, double, std::string, bool, std::shared_ptr<jsi::HostObject>>;
18
-
19
- /// @brief A registry for audio event handlers.
20
- /// It allows registering, unregistering, and invoking event handlers for audio events.
21
- /// State changes are performed only on the JS thread.
22
- /// State access is thread-safe via the RN CallInvoker.
23
+ /// @brief Registry for audio event handlers with built-in dispatcher.
24
+ ///
25
+ /// Handlers are registered/unregistered on the JS thread.
26
+ /// dispatchEvent() is lock-free and allocation-free can be called from the
27
+ /// Audio Thread.
23
28
  class AudioEventHandlerRegistry : public IAudioEventHandlerRegistry,
24
29
  public std::enable_shared_from_this<AudioEventHandlerRegistry> {
25
30
  public:
@@ -28,45 +33,59 @@ class AudioEventHandlerRegistry : public IAudioEventHandlerRegistry,
28
33
  const std::shared_ptr<react::CallInvoker> &callInvoker);
29
34
  ~AudioEventHandlerRegistry() override;
30
35
 
31
- /// @brief Registers an event handler for a specific audio event.
32
- /// @param eventName The name of the audio event.
33
- /// @param handler The JavaScript function to be called when the event occurs.
34
- /// @return A unique listener ID for the registered handler.
35
- /// @note Thread safe
36
+ DELETE_COPY_AND_MOVE(AudioEventHandlerRegistry);
37
+
36
38
  uint64_t registerHandler(AudioEvent eventName, const std::shared_ptr<jsi::Function> &handler)
37
39
  override;
38
40
 
39
- /// @brief Unregisters an event handler for a specific audio event using its listener ID.
40
- /// @param eventName The name of the audio event.
41
- /// @param listenerId The unique listener ID of the handler to be unregistered.
42
- /// @note Thread safe
43
41
  void unregisterHandler(AudioEvent eventName, uint64_t listenerId) override;
44
42
 
45
- /// @brief Invokes the event handler(s) for a specific event with the provided event body.
46
- /// @note Thread safe
47
- void invokeHandlerWithEventBody(
48
- AudioEvent eventName,
49
- const std::unordered_map<std::string, EventValue> &body) override;
50
-
51
- /// @brief Invokes the event handler(s) for a specific event with the provided event body.
52
- /// @note Thread safe
53
- void invokeHandlerWithEventBody(
43
+ bool dispatchEvent(
54
44
  AudioEvent eventName,
55
45
  uint64_t listenerId,
56
- const std::unordered_map<std::string, EventValue> &body) override;
46
+ AudioEventPayload &&payload) noexcept override;
57
47
 
58
48
  private:
59
- std::atomic<uint64_t> listenerIdCounter_{1}; // Atomic counter for listener IDs
49
+ static constexpr size_t kDispatchCapacity = 256;
50
+
51
+ struct DispatchEvent {
52
+ AudioEvent event{};
53
+ uint64_t listenerId{kBroadcastListenerId};
54
+ AudioEventPayload payload{EmptyPayload{}};
55
+ };
60
56
 
57
+ using Sender = channels::spsc::Sender<
58
+ DispatchEvent,
59
+ EVENT_DISPATCHER_SPSC_OVERFLOW_STRATEGY,
60
+ EVENT_DISPATCHER_SPSC_WAIT_STRATEGY>;
61
+ using Receiver = channels::spsc::Receiver<
62
+ DispatchEvent,
63
+ EVENT_DISPATCHER_SPSC_OVERFLOW_STRATEGY,
64
+ EVENT_DISPATCHER_SPSC_WAIT_STRATEGY>;
65
+
66
+ std::atomic<uint64_t> listenerIdCounter_{1};
61
67
  const std::shared_ptr<react::CallInvoker> callInvoker_;
62
68
  jsi::Runtime *runtime_;
63
69
  std::unordered_map<AudioEvent, std::unordered_map<uint64_t, std::shared_ptr<jsi::Function>>>
64
70
  eventHandlers_;
65
71
 
66
- jsi::Object createEventObject(const std::unordered_map<std::string, EventValue> &body);
67
- jsi::Object createEventObject(
68
- const std::unordered_map<std::string, EventValue> &body,
69
- size_t memoryPressure);
72
+ Sender sender_;
73
+ Receiver receiver_;
74
+ std::atomic<bool> isExiting_;
75
+ std::thread workerThread_;
76
+
77
+ void handleEventOnJSThread(
78
+ AudioEvent eventName,
79
+ uint64_t listenerId,
80
+ const AudioEventPayload &payload);
81
+ void invokeHandler(
82
+ const std::shared_ptr<jsi::Function> &handler,
83
+ const AudioEventPayload &payload);
84
+
85
+ jsi::Object buildJsiObject(const AudioEventPayload &payload) {
86
+ return std::visit(
87
+ [this](auto &&p) -> jsi::Object { return p.toJsiObject(*runtime_); }, payload);
88
+ }
70
89
  };
71
90
 
72
91
  } // namespace audioapi
@@ -0,0 +1,87 @@
1
+ #pragma once
2
+
3
+ #include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
4
+ #include <jsi/jsi.h>
5
+ #include <memory>
6
+ #include <string>
7
+ #include <variant>
8
+
9
+ namespace audioapi {
10
+
11
+ // broadcast sentinel — listenerId 0 means dispatch to all registered handlers.
12
+ static constexpr uint64_t kBroadcastListenerId = 0;
13
+
14
+ struct EmptyPayload {
15
+ facebook::jsi::Object toJsiObject(facebook::jsi::Runtime &rt) const {
16
+ return facebook::jsi::Object(rt);
17
+ }
18
+ };
19
+
20
+ struct DoubleValuePayload {
21
+ double value;
22
+
23
+ facebook::jsi::Object toJsiObject(facebook::jsi::Runtime &rt) const {
24
+ facebook::jsi::Object obj(rt);
25
+ obj.setProperty(rt, "value", value);
26
+ return obj;
27
+ }
28
+ };
29
+
30
+ struct InterruptionPayload {
31
+ std::string type;
32
+ bool shouldResume;
33
+
34
+ facebook::jsi::Object toJsiObject(facebook::jsi::Runtime &rt) const {
35
+ facebook::jsi::Object obj(rt);
36
+ obj.setProperty(rt, "type", facebook::jsi::String::createFromUtf8(rt, type));
37
+ obj.setProperty(rt, "shouldResume", shouldResume);
38
+ return obj;
39
+ }
40
+ };
41
+
42
+ struct StringPayload {
43
+ std::string name;
44
+ std::string reason;
45
+
46
+ facebook::jsi::Object toJsiObject(facebook::jsi::Runtime &rt) const {
47
+ facebook::jsi::Object obj(rt);
48
+ obj.setProperty(rt, name.c_str(), facebook::jsi::String::createFromUtf8(rt, reason));
49
+ return obj;
50
+ }
51
+ };
52
+
53
+ struct BufferEndedPayload {
54
+ size_t bufferId;
55
+ bool isLastBufferInQueue;
56
+
57
+ facebook::jsi::Object toJsiObject(facebook::jsi::Runtime &rt) const {
58
+ facebook::jsi::Object obj(rt);
59
+ obj.setProperty(
60
+ rt, "bufferId", facebook::jsi::String::createFromUtf8(rt, std::to_string(bufferId)));
61
+ obj.setProperty(rt, "isLastBufferInQueue", isLastBufferInQueue);
62
+ return obj;
63
+ }
64
+ };
65
+
66
+ struct AudioReadyPayload {
67
+ std::shared_ptr<AudioBufferHostObject> buffer;
68
+ int numFrames;
69
+
70
+ facebook::jsi::Object toJsiObject(facebook::jsi::Runtime &rt) const {
71
+ facebook::jsi::Object obj(rt);
72
+ obj.setProperty(rt, "buffer", facebook::jsi::Object::createFromHostObject(rt, buffer));
73
+ obj.setProperty(rt, "numFrames", numFrames);
74
+ obj.setExternalMemoryPressure(rt, buffer->getSizeInBytes());
75
+ return obj;
76
+ }
77
+ };
78
+
79
+ using AudioEventPayload = std::variant<
80
+ EmptyPayload,
81
+ DoubleValuePayload,
82
+ InterruptionPayload,
83
+ StringPayload,
84
+ BufferEndedPayload,
85
+ AudioReadyPayload>;
86
+
87
+ } // namespace audioapi