react-native-audio-api 0.11.0-nightly-6ba0571-20251210 → 0.11.0-nightly-c0ffb48-20251211
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.
- package/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +0 -13
- package/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +1 -1
- package/common/cpp/audioapi/core/effects/BiquadFilterNode.h +4 -4
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +2 -2
- package/common/cpp/audioapi/core/sources/StreamerNode.cpp +1 -0
- package/common/cpp/audioapi/core/sources/StreamerNode.h +1 -3
- package/common/cpp/audioapi/utils/AlignedAllocator.hpp +19 -16
- package/common/cpp/audioapi/utils/Benchmark.hpp +50 -47
- package/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +12 -9
- package/common/cpp/audioapi/utils/MoveOnlyFunction.hpp +17 -15
- package/common/cpp/audioapi/utils/RingBiDirectionalBuffer.hpp +22 -28
- package/common/cpp/audioapi/utils/SpscChannel.hpp +329 -305
- package/common/cpp/audioapi/utils/ThreadPool.hpp +54 -28
- package/common/cpp/test/CMakeLists.txt +2 -8
- package/package.json +2 -2
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#pragma once
|
|
2
|
-
#include <thread>
|
|
3
|
-
#include <vector>
|
|
4
2
|
#include <functional>
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <thread>
|
|
5
|
+
#include <utility>
|
|
5
6
|
#include <variant>
|
|
7
|
+
#include <vector>
|
|
6
8
|
|
|
7
9
|
#include <audioapi/utils/MoveOnlyFunction.hpp>
|
|
8
10
|
#include <audioapi/utils/SpscChannel.hpp>
|
|
@@ -17,7 +19,9 @@ namespace audioapi {
|
|
|
17
19
|
/// @note IMPORTANT: ThreadPool is not thread-safe and events should be scheduled from a single thread only.
|
|
18
20
|
class ThreadPool {
|
|
19
21
|
struct StopEvent {};
|
|
20
|
-
struct TaskEvent {
|
|
22
|
+
struct TaskEvent {
|
|
23
|
+
audioapi::move_only_function<void()> task;
|
|
24
|
+
};
|
|
21
25
|
using Event = std::variant<TaskEvent, StopEvent>;
|
|
22
26
|
|
|
23
27
|
struct Cntrl {
|
|
@@ -25,34 +29,51 @@ class ThreadPool {
|
|
|
25
29
|
std::atomic<size_t> tasksScheduled{0};
|
|
26
30
|
};
|
|
27
31
|
|
|
28
|
-
using Sender = channels::spsc::Sender<
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
using Sender = channels::spsc::Sender<
|
|
33
|
+
Event,
|
|
34
|
+
channels::spsc::OverflowStrategy::WAIT_ON_FULL,
|
|
35
|
+
channels::spsc::WaitStrategy::ATOMIC_WAIT>;
|
|
36
|
+
using Receiver = channels::spsc::Receiver<
|
|
37
|
+
Event,
|
|
38
|
+
channels::spsc::OverflowStrategy::WAIT_ON_FULL,
|
|
39
|
+
channels::spsc::WaitStrategy::ATOMIC_WAIT>;
|
|
40
|
+
|
|
41
|
+
public:
|
|
31
42
|
/// @brief Construct a new ThreadPool
|
|
32
43
|
/// @param numThreads The number of worker threads to create
|
|
33
44
|
/// @param loadBalancerQueueSize The size of the load balancer's queue
|
|
34
45
|
/// @param workerQueueSize The size of each worker thread's queue
|
|
35
|
-
ThreadPool(
|
|
36
|
-
|
|
46
|
+
explicit ThreadPool(
|
|
47
|
+
size_t numThreads,
|
|
48
|
+
size_t loadBalancerQueueSize = 32,
|
|
49
|
+
size_t workerQueueSize = 32) {
|
|
50
|
+
auto [sender, receiver] = channels::spsc::channel<
|
|
51
|
+
Event,
|
|
52
|
+
channels::spsc::OverflowStrategy::WAIT_ON_FULL,
|
|
53
|
+
channels::spsc::WaitStrategy::ATOMIC_WAIT>(loadBalancerQueueSize);
|
|
37
54
|
loadBalancerSender = std::move(sender);
|
|
38
55
|
std::vector<Sender> workerSenders;
|
|
39
56
|
workerSenders.reserve(numThreads);
|
|
40
57
|
for (size_t i = 0; i < numThreads; ++i) {
|
|
41
|
-
auto [workerSender, workerReceiver] = channels::spsc::channel<
|
|
58
|
+
auto [workerSender, workerReceiver] = channels::spsc::channel<
|
|
59
|
+
Event,
|
|
60
|
+
channels::spsc::OverflowStrategy::WAIT_ON_FULL,
|
|
61
|
+
channels::spsc::WaitStrategy::ATOMIC_WAIT>(workerQueueSize);
|
|
42
62
|
workers.emplace_back(&ThreadPool::workerThreadFunc, this, std::move(workerReceiver));
|
|
43
63
|
workerSenders.emplace_back(std::move(workerSender));
|
|
44
64
|
}
|
|
45
|
-
loadBalancerThread = std::thread(
|
|
65
|
+
loadBalancerThread = std::thread(
|
|
66
|
+
&ThreadPool::loadBalancerThreadFunc, this, std::move(receiver), std::move(workerSenders));
|
|
46
67
|
controlBlock_ = std::make_unique<Cntrl>();
|
|
47
68
|
}
|
|
48
|
-
ThreadPool(const ThreadPool&) = delete;
|
|
49
|
-
ThreadPool&
|
|
50
|
-
ThreadPool(ThreadPool&&
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
ThreadPool&
|
|
69
|
+
ThreadPool(const ThreadPool &) = delete;
|
|
70
|
+
ThreadPool &operator=(const ThreadPool &) = delete;
|
|
71
|
+
ThreadPool(ThreadPool &&other)
|
|
72
|
+
: loadBalancerThread(std::move(other.loadBalancerThread)),
|
|
73
|
+
workers(std::move(other.workers)),
|
|
74
|
+
loadBalancerSender(std::move(other.loadBalancerSender)),
|
|
75
|
+
controlBlock_(std::move(other.controlBlock_)) {}
|
|
76
|
+
ThreadPool &operator=(ThreadPool &&other) {
|
|
56
77
|
if (this != &other) {
|
|
57
78
|
loadBalancerThread = std::move(other.loadBalancerThread);
|
|
58
79
|
workers = std::move(other.workers);
|
|
@@ -69,7 +90,7 @@ public:
|
|
|
69
90
|
}
|
|
70
91
|
loadBalancerSender.send(StopEvent{});
|
|
71
92
|
loadBalancerThread.join();
|
|
72
|
-
for (auto&
|
|
93
|
+
for (auto &worker : workers) {
|
|
73
94
|
worker.join();
|
|
74
95
|
}
|
|
75
96
|
}
|
|
@@ -84,14 +105,19 @@ public:
|
|
|
84
105
|
/// @note The task should not throw exceptions, as they will not be caught.
|
|
85
106
|
/// @note The task should end at some point, otherwise the thread pool will never be able to shut down.
|
|
86
107
|
/// @note IMPORTANT: This function is not thread-safe and should be called from a single thread only.
|
|
87
|
-
template
|
|
88
|
-
|
|
108
|
+
template <
|
|
109
|
+
typename Func,
|
|
110
|
+
typename... Args,
|
|
111
|
+
typename = std::enable_if_t<std::is_invocable_r_v<void, Func, Args...>>>
|
|
112
|
+
void schedule(Func &&task, Args &&...args) noexcept {
|
|
89
113
|
controlBlock_->tasksScheduled.fetch_add(1, std::memory_order_release);
|
|
90
114
|
|
|
91
115
|
/// We know that lifetime of each worker thus spsc thus lambda is strongly bounded by ThreadPool lifetime
|
|
92
116
|
/// so we can safely capture control block pointer unsafely here
|
|
93
117
|
Cntrl *cntrl = controlBlock_.get();
|
|
94
|
-
auto boundTask = [cntrl,
|
|
118
|
+
auto boundTask = [cntrl,
|
|
119
|
+
f = std::forward<Func>(task),
|
|
120
|
+
... capturedArgs = std::forward<Args>(args)]() mutable {
|
|
95
121
|
f(std::forward<Args>(capturedArgs)...);
|
|
96
122
|
size_t left = cntrl->tasksScheduled.fetch_sub(1, std::memory_order_acq_rel) - 1;
|
|
97
123
|
if (left == 0) {
|
|
@@ -121,7 +147,7 @@ public:
|
|
|
121
147
|
return;
|
|
122
148
|
}
|
|
123
149
|
|
|
124
|
-
private:
|
|
150
|
+
private:
|
|
125
151
|
std::thread loadBalancerThread;
|
|
126
152
|
std::vector<std::thread> workers;
|
|
127
153
|
Sender loadBalancerSender;
|
|
@@ -135,9 +161,9 @@ private:
|
|
|
135
161
|
/// We use [[unlikely]] and [[likely]] attributes to help the compiler optimize the branching.
|
|
136
162
|
/// we expect most of the time to receive TaskEvent, and rarely StopEvent.
|
|
137
163
|
/// and whenever we receive StopEvent we can burn some cycles as it will not be expected to execute fast.
|
|
138
|
-
if (std::holds_alternative<StopEvent>(event)) [[
|
|
164
|
+
if (std::holds_alternative<StopEvent>(event)) [[unlikely]] {
|
|
139
165
|
break;
|
|
140
|
-
} else if (std::holds_alternative<TaskEvent>(event)) [[
|
|
166
|
+
} else if (std::holds_alternative<TaskEvent>(event)) [[likely]] {
|
|
141
167
|
std::get<TaskEvent>(event).task();
|
|
142
168
|
}
|
|
143
169
|
}
|
|
@@ -152,15 +178,15 @@ private:
|
|
|
152
178
|
/// We use [[unlikely]] and [[likely]] attributes to help the compiler optimize the branching.
|
|
153
179
|
/// we expect most of the time to receive TaskEvent, and rarely StopEvent.
|
|
154
180
|
/// and whenever we receive StopEvent we can burn some cycles as it will not be expected to execute fast.
|
|
155
|
-
if (std::holds_alternative<StopEvent>(event)) [[
|
|
181
|
+
if (std::holds_alternative<StopEvent>(event)) [[unlikely]] {
|
|
156
182
|
// Propagate stop event to all workers
|
|
157
183
|
for (size_t i = 0; i < localWorkerSenders.size(); ++i) {
|
|
158
184
|
localWorkerSenders[i].send(StopEvent{});
|
|
159
185
|
}
|
|
160
186
|
break;
|
|
161
|
-
} else if (std::holds_alternative<TaskEvent>(event)) [[
|
|
187
|
+
} else if (std::holds_alternative<TaskEvent>(event)) [[likely]] {
|
|
162
188
|
// Dispatch task to the next worker in round-robin fashion
|
|
163
|
-
auto&
|
|
189
|
+
auto &taskEvent = std::get<TaskEvent>(event);
|
|
164
190
|
localWorkerSenders[nextWorker].send(std::move(taskEvent));
|
|
165
191
|
nextWorker = (nextWorker + 1) % localWorkerSenders.size();
|
|
166
192
|
}
|
|
@@ -24,21 +24,16 @@ set(REACT_NATIVE_AUDIO_API_DIR "${ROOT}/node_modules/react-native-audio-api")
|
|
|
24
24
|
file(GLOB_RECURSE RNAUDIOAPI_SRC
|
|
25
25
|
CONFIGURE_DEPENDS
|
|
26
26
|
"${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/*.cpp"
|
|
27
|
-
"${REACT_NATIVE_AUDIO_API_DIR}/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp"
|
|
28
27
|
)
|
|
29
28
|
|
|
30
29
|
# exclude HostObjects from tests
|
|
31
30
|
list(FILTER RNAUDIOAPI_SRC EXCLUDE REGEX ".*/audioapi/HostObjects/.*\\.cpp$")
|
|
31
|
+
# exclude worklet nodes
|
|
32
|
+
list(FILTER RNAUDIOAPI_SRC EXCLUDE REGEX ".*/Worklet.*Node\\.cpp$")
|
|
32
33
|
|
|
33
34
|
list(REMOVE_ITEM RNAUDIOAPI_SRC
|
|
34
35
|
"${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/core/AudioContext.cpp"
|
|
35
|
-
"${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/core/effects/WorkletNode.cpp"
|
|
36
|
-
"${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp"
|
|
37
|
-
"${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp"
|
|
38
|
-
"${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/core/sources/StreamerNode.cpp"
|
|
39
|
-
"${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/core/sources/StreamerNode.h"
|
|
40
36
|
"${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp"
|
|
41
|
-
"${REACT_NATIVE_AUDIO_API_DIR}/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h"
|
|
42
37
|
)
|
|
43
38
|
|
|
44
39
|
file(GLOB_RECURSE RNAUDIOAPI_LIBS
|
|
@@ -78,7 +73,6 @@ add_executable(
|
|
|
78
73
|
${test_src}
|
|
79
74
|
)
|
|
80
75
|
|
|
81
|
-
add_compile_definitions(AUDIO_API_TEST_SUITE)
|
|
82
76
|
add_compile_definitions(RN_AUDIO_API_ENABLE_WORKLETS=0)
|
|
83
77
|
add_compile_definitions(RN_AUDIO_API_TEST=1)
|
|
84
78
|
add_compile_definitions(RN_AUDIO_API_FFMPEG_DISABLED=1)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-audio-api",
|
|
3
|
-
"version": "0.11.0-nightly-
|
|
3
|
+
"version": "0.11.0-nightly-c0ffb48-20251211",
|
|
4
4
|
"description": "react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification",
|
|
5
5
|
"bin": {
|
|
6
6
|
"setup-rn-audio-api-web": "./scripts/setup-rn-audio-api-web.js"
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"format:android:cpp": "find android/src/ -path android/src/main/cpp/audioapi/android/libs -iname \"*.h\" -o -iname \"*.cpp\" | xargs clang-format -i",
|
|
54
54
|
"format:android:kotlin": "ktlint -F 'android/src/main/java/**/*.kt'",
|
|
55
55
|
"format:ios": "find ios/audioapi/ios -iname \"*.h\" -o -iname \"*.m\" -o -iname \"*.mm\" | xargs clang-format -i",
|
|
56
|
-
"format:common": "find common/cpp/ -path 'common/cpp/audioapi/libs' -prune -o -path 'common/cpp/audioapi/external' -prune -o -type f -iname \"*.h\" -print -o -type f -iname \"*.cpp\" -print | xargs clang-format -i",
|
|
56
|
+
"format:common": "find common/cpp/ -path 'common/cpp/audioapi/libs' -prune -o -path 'common/cpp/audioapi/external' -prune -o -type f -iname \"*.h\" -print -o -type f -iname \"*.cpp\" -o -type f -iname \"*.hpp\" -print | xargs clang-format -i",
|
|
57
57
|
"build": "bob build",
|
|
58
58
|
"create:package": "./scripts/create-package.sh",
|
|
59
59
|
"prepack": "cp ../../README.md ./README.md",
|