react-native-audio-api 0.11.0-nightly-6ba0571-20251209 → 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
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
#include <audioapi/libs/miniaudio/decoders/libvorbis/miniaudio_libvorbis.h>
|
|
11
11
|
#include <audioapi/libs/miniaudio/miniaudio.h>
|
|
12
12
|
|
|
13
|
-
#ifndef AUDIO_API_TEST_SUITE
|
|
14
13
|
#include <android/log.h>
|
|
15
|
-
#endif // AUDIO_API_TEST_SUITE
|
|
16
14
|
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
17
15
|
#include <audioapi/libs/ffmpeg/FFmpegDecoding.h>
|
|
18
16
|
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
@@ -31,7 +29,6 @@ std::vector<float> AudioDecoder::readAllPcmFrames(ma_decoder &decoder, int outpu
|
|
|
31
29
|
std::vector<float> temp(CHUNK_SIZE * outputChannels);
|
|
32
30
|
ma_uint64 outFramesRead = 0;
|
|
33
31
|
|
|
34
|
-
#ifndef AUDIO_API_TEST_SUITE
|
|
35
32
|
while (true) {
|
|
36
33
|
ma_uint64 tempFramesDecoded = 0;
|
|
37
34
|
ma_decoder_read_pcm_frames(&decoder, temp.data(), CHUNK_SIZE, &tempFramesDecoded);
|
|
@@ -46,8 +43,6 @@ std::vector<float> AudioDecoder::readAllPcmFrames(ma_decoder &decoder, int outpu
|
|
|
46
43
|
if (outFramesRead == 0) {
|
|
47
44
|
__android_log_print(ANDROID_LOG_ERROR, "AudioDecoder", "Failed to decode");
|
|
48
45
|
}
|
|
49
|
-
#endif
|
|
50
|
-
return buffer;
|
|
51
46
|
}
|
|
52
47
|
|
|
53
48
|
std::shared_ptr<AudioBuffer> AudioDecoder::makeAudioBufferFromFloatBuffer(
|
|
@@ -73,7 +68,6 @@ std::shared_ptr<AudioBuffer> AudioDecoder::makeAudioBufferFromFloatBuffer(
|
|
|
73
68
|
std::shared_ptr<AudioBuffer> AudioDecoder::decodeWithFilePath(
|
|
74
69
|
const std::string &path,
|
|
75
70
|
float sampleRate) {
|
|
76
|
-
#ifndef AUDIO_API_TEST_SUITE
|
|
77
71
|
if (AudioDecoder::pathHasExtension(path, {".mp4", ".m4a", ".aac"})) {
|
|
78
72
|
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
79
73
|
auto buffer = ffmpegdecoder::decodeWithFilePath(path, static_cast<int>(sampleRate));
|
|
@@ -116,14 +110,10 @@ std::shared_ptr<AudioBuffer> AudioDecoder::decodeWithFilePath(
|
|
|
116
110
|
std::vector<float> buffer = readAllPcmFrames(decoder, outputChannels);
|
|
117
111
|
ma_decoder_uninit(&decoder);
|
|
118
112
|
return makeAudioBufferFromFloatBuffer(buffer, outputSampleRate, outputChannels);
|
|
119
|
-
#else
|
|
120
|
-
return nullptr;
|
|
121
|
-
#endif
|
|
122
113
|
}
|
|
123
114
|
|
|
124
115
|
std::shared_ptr<AudioBuffer>
|
|
125
116
|
AudioDecoder::decodeWithMemoryBlock(const void *data, size_t size, float sampleRate) {
|
|
126
|
-
#ifndef AUDIO_API_TEST_SUITE
|
|
127
117
|
const AudioFormat format = AudioDecoder::detectAudioFormat(data, size);
|
|
128
118
|
if (format == AudioFormat::MP4 || format == AudioFormat::M4A || format == AudioFormat::AAC) {
|
|
129
119
|
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
@@ -161,9 +151,6 @@ AudioDecoder::decodeWithMemoryBlock(const void *data, size_t size, float sampleR
|
|
|
161
151
|
std::vector<float> buffer = readAllPcmFrames(decoder, outputChannels);
|
|
162
152
|
ma_decoder_uninit(&decoder);
|
|
163
153
|
return makeAudioBufferFromFloatBuffer(buffer, outputSampleRate, outputChannels);
|
|
164
|
-
#else
|
|
165
|
-
return nullptr;
|
|
166
|
-
#endif
|
|
167
154
|
}
|
|
168
155
|
|
|
169
156
|
std::shared_ptr<AudioBuffer> AudioDecoder::decodeWithPCMInBase64(
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
#include <audioapi/core/AudioNode.h>
|
|
32
32
|
#include <audioapi/core/AudioParam.h>
|
|
33
33
|
#include <audioapi/core/types/BiquadFilterType.h>
|
|
34
|
-
#
|
|
34
|
+
#if RN_AUDIO_API_TEST
|
|
35
35
|
#include <gtest/gtest_prod.h>
|
|
36
|
-
#endif
|
|
36
|
+
#endif // RN_AUDIO_API_TEST
|
|
37
37
|
|
|
38
38
|
#include <algorithm>
|
|
39
39
|
#include <cmath>
|
|
@@ -48,10 +48,10 @@ namespace audioapi {
|
|
|
48
48
|
class AudioBus;
|
|
49
49
|
|
|
50
50
|
class BiquadFilterNode : public AudioNode {
|
|
51
|
-
#
|
|
51
|
+
#if RN_AUDIO_API_TEST
|
|
52
52
|
friend class BiquadFilterTest;
|
|
53
53
|
FRIEND_TEST(BiquadFilterTest, GetFrequencyResponse);
|
|
54
|
-
#endif
|
|
54
|
+
#endif // RN_AUDIO_API_TEST
|
|
55
55
|
|
|
56
56
|
public:
|
|
57
57
|
explicit BiquadFilterNode(BaseAudioContext *context);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
#if !RN_AUDIO_API_TEST
|
|
10
10
|
#include <audioapi/core/AudioContext.h>
|
|
11
|
-
#endif
|
|
11
|
+
#endif // RN_AUDIO_API_TEST
|
|
12
12
|
|
|
13
13
|
#include <algorithm>
|
|
14
14
|
#include <limits>
|
|
@@ -30,7 +30,7 @@ void AudioScheduledSourceNode::start(double when) {
|
|
|
30
30
|
if (auto context = dynamic_cast<AudioContext *>(context_)) {
|
|
31
31
|
context->start();
|
|
32
32
|
}
|
|
33
|
-
#endif
|
|
33
|
+
#endif // RN_AUDIO_API_TEST
|
|
34
34
|
|
|
35
35
|
playbackState_ = PlaybackState::SCHEDULED;
|
|
36
36
|
startTime_ = when;
|
|
@@ -294,6 +294,7 @@ bool StreamerNode::setupDecoder() {
|
|
|
294
294
|
|
|
295
295
|
void StreamerNode::cleanup() {
|
|
296
296
|
this->playbackState_ = PlaybackState::FINISHED;
|
|
297
|
+
isNodeFinished_.store(true, std::memory_order_release);
|
|
297
298
|
if (streamingThread_.joinable()) {
|
|
298
299
|
StreamingData dummy;
|
|
299
300
|
while (receiver_.try_receive(dummy) == channels::spsc::ResponseStatus::SUCCESS)
|
|
@@ -24,19 +24,17 @@ extern "C" {
|
|
|
24
24
|
}
|
|
25
25
|
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
26
26
|
|
|
27
|
+
#include <audioapi/utils/SpscChannel.hpp>
|
|
27
28
|
#include <atomic>
|
|
28
29
|
#include <cmath>
|
|
29
30
|
#include <memory>
|
|
30
31
|
#include <string>
|
|
31
32
|
#include <utility>
|
|
32
|
-
#ifndef AUDIO_API_TEST_SUITE
|
|
33
|
-
#include <audioapi/utils/SpscChannel.hpp>
|
|
34
33
|
|
|
35
34
|
static constexpr audioapi::channels::spsc::OverflowStrategy STREAMER_NODE_SPSC_OVERFLOW_STRATEGY =
|
|
36
35
|
audioapi::channels::spsc::OverflowStrategy::WAIT_ON_FULL;
|
|
37
36
|
static constexpr audioapi::channels::spsc::WaitStrategy STREAMER_NODE_SPSC_WAIT_STRATEGY =
|
|
38
37
|
audioapi::channels::spsc::WaitStrategy::ATOMIC_WAIT;
|
|
39
|
-
#endif // AUDIO_API_TEST_SUITE
|
|
40
38
|
|
|
41
39
|
static constexpr bool VERBOSE = false;
|
|
42
40
|
static constexpr int CHANNEL_CAPACITY = 32;
|
|
@@ -2,49 +2,52 @@
|
|
|
2
2
|
#include <cstddef>
|
|
3
3
|
#include <new>
|
|
4
4
|
|
|
5
|
-
template<typename T, std::size_t Align = 16>
|
|
5
|
+
template <typename T, std::size_t Align = 16>
|
|
6
6
|
class AlignedAllocator {
|
|
7
|
-
public:
|
|
7
|
+
public:
|
|
8
8
|
using value_type = T;
|
|
9
9
|
using size_type = std::size_t;
|
|
10
10
|
using difference_type = std::ptrdiff_t;
|
|
11
11
|
|
|
12
12
|
AlignedAllocator() noexcept = default;
|
|
13
|
-
template<class U>
|
|
13
|
+
template <class U>
|
|
14
|
+
AlignedAllocator(const AlignedAllocator<U, Align> &) noexcept {}
|
|
14
15
|
|
|
15
|
-
T*
|
|
16
|
+
T *allocate(std::size_t n) {
|
|
16
17
|
// We want to maximize performance on hot paths, so we hint unlikely branches
|
|
17
|
-
if (n == 0) [[
|
|
18
|
+
if (n == 0) [[unlikely]] {
|
|
18
19
|
return nullptr;
|
|
19
20
|
}
|
|
20
21
|
std::size_t bytes = n * sizeof(T);
|
|
21
22
|
// C++17 aligned new
|
|
22
|
-
void*
|
|
23
|
+
void *p = ::operator new(bytes, std::align_val_t(Align));
|
|
23
24
|
|
|
24
25
|
// We have more serious problems if this happens than speed concerns
|
|
25
26
|
// so we can opt the branch prediction
|
|
26
|
-
if (!p) [[
|
|
27
|
+
if (!p) [[unlikely]] {
|
|
27
28
|
throw std::bad_alloc();
|
|
28
29
|
}
|
|
29
|
-
return static_cast<T*>(p);
|
|
30
|
+
return static_cast<T *>(p);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
void deallocate(T*
|
|
33
|
-
|
|
33
|
+
void deallocate(T *p, std::size_t) noexcept {
|
|
34
|
+
::operator delete(p, std::align_val_t(Align));
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
// Rebind allocator to type U (required by std::vector)
|
|
37
|
-
template<class U>
|
|
38
|
-
struct rebind {
|
|
38
|
+
template <class U>
|
|
39
|
+
struct rebind {
|
|
40
|
+
using other = AlignedAllocator<U, Align>;
|
|
41
|
+
};
|
|
39
42
|
|
|
40
43
|
// Comparison operators (required by std::vector)
|
|
41
|
-
template<typename U, std::size_t UAlign>
|
|
42
|
-
bool operator==(const AlignedAllocator<U, UAlign
|
|
44
|
+
template <typename U, std::size_t UAlign>
|
|
45
|
+
bool operator==(const AlignedAllocator<U, UAlign> &) const noexcept {
|
|
43
46
|
return Align == UAlign;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
template<typename U, std::size_t UAlign>
|
|
47
|
-
bool operator!=(const AlignedAllocator<U, UAlign
|
|
49
|
+
template <typename U, std::size_t UAlign>
|
|
50
|
+
bool operator!=(const AlignedAllocator<U, UAlign> &) const noexcept {
|
|
48
51
|
return Align != UAlign;
|
|
49
52
|
}
|
|
50
53
|
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Benchmarking utilities
|
|
4
4
|
#include <chrono>
|
|
5
|
+
#include <cstdio>
|
|
6
|
+
#include <string>
|
|
5
7
|
#include <unordered_map>
|
|
8
|
+
#include <utility>
|
|
6
9
|
|
|
7
10
|
#ifdef ANDROID
|
|
8
11
|
#include <android/log.h>
|
|
@@ -10,49 +13,49 @@
|
|
|
10
13
|
|
|
11
14
|
namespace audioapi::benchmarks {
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
16
|
+
/// @brief Gets the execution time of a function
|
|
17
|
+
/// @tparam Func The type of the function to benchmark
|
|
18
|
+
/// @param func The function to benchmark
|
|
19
|
+
/// @return The duration of the function execution in nanoseconds
|
|
20
|
+
/// @note This function is safe to use across threads
|
|
21
|
+
template <typename Func>
|
|
22
|
+
double getExecutionTime(Func &&func) {
|
|
23
|
+
auto start = std::chrono::high_resolution_clock::now();
|
|
24
|
+
std::forward<Func>(func)();
|
|
25
|
+
auto end = std::chrono::high_resolution_clock::now();
|
|
26
|
+
return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// @brief Logs the average execution time of a function
|
|
30
|
+
/// @tparam Func The type of the function to benchmark
|
|
31
|
+
/// @param msg The message to log
|
|
32
|
+
/// @param func The function to benchmark
|
|
33
|
+
/// @return The duration of the function execution in nanoseconds
|
|
34
|
+
/// @note This function should not be used in production
|
|
35
|
+
/// @note This function is not thread-safe and should be used preferably once in a codebase when you need to measure performance
|
|
36
|
+
template <typename Func>
|
|
37
|
+
double logAvgExecutionTime(const std::string &msg, Func &&func) {
|
|
38
|
+
double duration = getExecutionTime(std::forward<Func>(func));
|
|
39
|
+
|
|
40
|
+
static std::unordered_map<std::string, double> durationsSum;
|
|
41
|
+
static std::unordered_map<std::string, int> durationsCount;
|
|
42
|
+
|
|
43
|
+
// Ensure initialization for first time
|
|
44
|
+
if (durationsSum.find(msg) == durationsSum.end()) {
|
|
45
|
+
durationsSum[msg] = 0.0;
|
|
46
|
+
durationsCount[msg] = 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
durationsSum[msg] += duration;
|
|
50
|
+
durationsCount[msg]++;
|
|
51
|
+
|
|
52
|
+
int64_t avgDuration = static_cast<int64_t>(durationsSum[msg] / durationsCount[msg]);
|
|
53
|
+
|
|
54
|
+
#ifdef ANDROID
|
|
55
|
+
__android_log_print(ANDROID_LOG_INFO, "AudioAPI", "%s: %lld ns", msg.c_str(), avgDuration);
|
|
56
|
+
#else
|
|
57
|
+
printf("%s: %lld ns\n", msg.c_str(), avgDuration);
|
|
58
|
+
#endif
|
|
59
|
+
return duration;
|
|
60
|
+
}
|
|
61
|
+
} // namespace audioapi::benchmarks
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
#include <audioapi/utils/SpscChannel.hpp>
|
|
4
4
|
|
|
5
|
-
#include <type_traits>
|
|
6
5
|
#include <functional>
|
|
6
|
+
#include <type_traits>
|
|
7
|
+
#include <utility>
|
|
7
8
|
|
|
8
9
|
namespace audioapi {
|
|
9
10
|
using namespace channels::spsc;
|
|
@@ -27,32 +28,34 @@ template <typename T>
|
|
|
27
28
|
class CrossThreadEventScheduler {
|
|
28
29
|
public:
|
|
29
30
|
explicit CrossThreadEventScheduler(size_t capacity) {
|
|
30
|
-
auto [sender, receiver] = channel<std::function<void(T&)>>(capacity);
|
|
31
|
+
auto [sender, receiver] = channel<std::function<void(T &)>>(capacity);
|
|
31
32
|
eventSender_ = std::move(sender);
|
|
32
33
|
eventReceiver_ = std::move(receiver);
|
|
33
34
|
}
|
|
34
|
-
CrossThreadEventScheduler(const CrossThreadEventScheduler&) = delete;
|
|
35
|
-
CrossThreadEventScheduler&
|
|
35
|
+
CrossThreadEventScheduler(const CrossThreadEventScheduler &) = delete;
|
|
36
|
+
CrossThreadEventScheduler &operator=(const CrossThreadEventScheduler &) = delete;
|
|
36
37
|
|
|
37
38
|
/// @brief Schedules an event to be processed on the audio thread.
|
|
38
39
|
/// @param event The event to schedule.
|
|
39
40
|
/// @return True if the event was successfully scheduled, false if the queue is full.
|
|
40
|
-
bool scheduleEvent(std::function<void(T&)> &&event) noexcept(
|
|
41
|
+
bool scheduleEvent(std::function<void(T &)> &&event) noexcept(
|
|
42
|
+
noexcept(eventSender_.try_send(std::move(event)))) {
|
|
41
43
|
return eventSender_.try_send(std::move(event)) == ResponseStatus::SUCCESS;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
/// @brief Processes all scheduled events.
|
|
45
47
|
/// @param data The data to pass to each event.
|
|
46
|
-
void processAllEvents(T&
|
|
47
|
-
|
|
48
|
+
void processAllEvents(T &data) noexcept(
|
|
49
|
+
noexcept(eventReceiver_.try_receive(std::declval<std::function<void(T &)> &>()))) {
|
|
50
|
+
std::function<void(T &)> event;
|
|
48
51
|
while (eventReceiver_.try_receive(event) == ResponseStatus::SUCCESS) {
|
|
49
52
|
event(data);
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
private:
|
|
54
|
-
Sender<std::function<void(T&)>> eventSender_;
|
|
55
|
-
Receiver<std::function<void(T&)>> eventReceiver_;
|
|
57
|
+
Sender<std::function<void(T &)>> eventSender_;
|
|
58
|
+
Receiver<std::function<void(T &)>> eventReceiver_;
|
|
56
59
|
};
|
|
57
60
|
|
|
58
61
|
} // namespace audioapi
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
#include <functional>
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <utility>
|
|
3
5
|
|
|
4
6
|
namespace audioapi {
|
|
5
7
|
|
|
6
8
|
/// @brief A forward declaration of a move-only function wrapper.
|
|
7
9
|
/// @note it is somehow required to have <R(Args...)> specialization below
|
|
8
10
|
/// @tparam Signature
|
|
9
|
-
template<typename Signature>
|
|
11
|
+
template <typename Signature>
|
|
10
12
|
class move_only_function; // Forward declaration
|
|
11
13
|
|
|
12
14
|
/// @brief A move-only function wrapper similar to std::function but non-copyable.
|
|
@@ -18,7 +20,7 @@ class move_only_function; // Forward declaration
|
|
|
18
20
|
/// @note IMPORTANT: This thing is implemented in C++23 standard and can be replaced with std::move_only_function once we switch to C++23.
|
|
19
21
|
/// @tparam R
|
|
20
22
|
/// @tparam ...Args
|
|
21
|
-
template<typename R, typename... Args>
|
|
23
|
+
template <typename R, typename... Args>
|
|
22
24
|
class move_only_function<R(Args...)> {
|
|
23
25
|
/// @brief The base class for type erasure.
|
|
24
26
|
/// @note It gets optimized by Empty Base Optimization (EBO) when possible.
|
|
@@ -29,7 +31,7 @@ class move_only_function<R(Args...)> {
|
|
|
29
31
|
|
|
30
32
|
/// @brief The implementation of the callable object.
|
|
31
33
|
/// @tparam F
|
|
32
|
-
template<typename F>
|
|
34
|
+
template <typename F>
|
|
33
35
|
struct callable_impl : callable_base {
|
|
34
36
|
/// @brief The stored callable object.
|
|
35
37
|
F f;
|
|
@@ -38,8 +40,8 @@ class move_only_function<R(Args...)> {
|
|
|
38
40
|
/// @tparam G
|
|
39
41
|
/// @param func
|
|
40
42
|
/// @note The enable_if_t ensures that F can be constructed from G&&.
|
|
41
|
-
template<typename G, typename = std::enable_if_t<std::is_constructible_v<F, G&&>>>
|
|
42
|
-
callable_impl(G&&
|
|
43
|
+
template <typename G, typename = std::enable_if_t<std::is_constructible_v<F, G &&>>>
|
|
44
|
+
explicit callable_impl(G &&func) : f(std::forward<G>(func)) {}
|
|
43
45
|
|
|
44
46
|
/// @brief Invoke the stored callable object with the given arguments.
|
|
45
47
|
/// @param args
|
|
@@ -58,19 +60,19 @@ class move_only_function<R(Args...)> {
|
|
|
58
60
|
/// @brief The unique pointer to the base callable type.
|
|
59
61
|
std::unique_ptr<callable_base> impl_;
|
|
60
62
|
|
|
61
|
-
public:
|
|
63
|
+
public:
|
|
62
64
|
move_only_function() = default;
|
|
63
65
|
move_only_function(std::nullptr_t) noexcept : impl_(nullptr) {}
|
|
64
66
|
|
|
65
|
-
template<typename F>
|
|
66
|
-
move_only_function(F&&
|
|
67
|
-
|
|
67
|
+
template <typename F>
|
|
68
|
+
move_only_function(F &&f)
|
|
69
|
+
: impl_(std::make_unique<callable_impl<std::decay_t<F>>>(std::forward<F>(f))) {}
|
|
68
70
|
|
|
69
|
-
move_only_function(const move_only_function&) = delete;
|
|
70
|
-
move_only_function&
|
|
71
|
+
move_only_function(const move_only_function &) = delete;
|
|
72
|
+
move_only_function &operator=(const move_only_function &) = delete;
|
|
71
73
|
|
|
72
|
-
move_only_function(move_only_function&&) = default;
|
|
73
|
-
move_only_function&
|
|
74
|
+
move_only_function(move_only_function &&) = default;
|
|
75
|
+
move_only_function &operator=(move_only_function &&) = default;
|
|
74
76
|
|
|
75
77
|
inline explicit operator bool() const noexcept {
|
|
76
78
|
return impl_ != nullptr;
|
|
@@ -78,13 +80,13 @@ public:
|
|
|
78
80
|
|
|
79
81
|
inline R operator()(Args... args) {
|
|
80
82
|
/// We are unlikely to hit this case as we want to optimize for the common case.
|
|
81
|
-
if (impl_ == nullptr) [[
|
|
83
|
+
if (impl_ == nullptr) [[unlikely]] {
|
|
82
84
|
throw std::bad_function_call{};
|
|
83
85
|
}
|
|
84
86
|
return (*impl_)(std::forward<Args>(args)...);
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
void swap(move_only_function&
|
|
89
|
+
void swap(move_only_function &other) noexcept {
|
|
88
90
|
impl_.swap(other.impl_);
|
|
89
91
|
}
|
|
90
92
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <bit>
|
|
4
4
|
#include <memory>
|
|
5
5
|
#include <type_traits>
|
|
6
|
+
#include <utility>
|
|
6
7
|
|
|
7
8
|
namespace audioapi {
|
|
8
9
|
|
|
@@ -16,15 +17,10 @@ template <typename T, size_t capacity_>
|
|
|
16
17
|
class RingBiDirectionalBuffer {
|
|
17
18
|
public:
|
|
18
19
|
/// @brief Constructor for RingBuffer.
|
|
19
|
-
RingBiDirectionalBuffer()
|
|
20
|
-
: headIndex_(0), tailIndex_(0) {
|
|
20
|
+
RingBiDirectionalBuffer() : headIndex_(0), tailIndex_(0) {
|
|
21
21
|
static_assert(isPowerOfTwo(capacity_), "RingBiDirectionalBuffer's capacity must be power of 2");
|
|
22
|
-
buffer_ = static_cast<T*>(
|
|
23
|
-
|
|
24
|
-
capacity_ * sizeof(T),
|
|
25
|
-
static_cast<std::align_val_t>(alignof(T))
|
|
26
|
-
)
|
|
27
|
-
);
|
|
22
|
+
buffer_ = static_cast<T *>(
|
|
23
|
+
::operator new[](capacity_ * sizeof(T), static_cast<std::align_val_t>(alignof(T))));
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
/// @brief Destructor for RingBuffer.
|
|
@@ -32,11 +28,7 @@ class RingBiDirectionalBuffer {
|
|
|
32
28
|
for (int i = headIndex_; i != tailIndex_; i = nextIndex(i)) {
|
|
33
29
|
buffer_[i].~T();
|
|
34
30
|
}
|
|
35
|
-
::operator delete[](
|
|
36
|
-
buffer_,
|
|
37
|
-
capacity_ * sizeof(T),
|
|
38
|
-
static_cast<std::align_val_t>(alignof(T))
|
|
39
|
-
);
|
|
31
|
+
::operator delete[](buffer_, capacity_ * sizeof(T), static_cast<std::align_val_t>(alignof(T)));
|
|
40
32
|
}
|
|
41
33
|
|
|
42
34
|
/// @brief Push a value into the ring buffer.
|
|
@@ -44,8 +36,8 @@ class RingBiDirectionalBuffer {
|
|
|
44
36
|
/// @param value The value to push.
|
|
45
37
|
/// @return True if the value was pushed successfully, false if the buffer is full.
|
|
46
38
|
template <typename U>
|
|
47
|
-
bool pushBack(U&&
|
|
48
|
-
if (isFull()) [[
|
|
39
|
+
bool pushBack(U &&value) noexcept(std::is_nothrow_constructible_v<T, U &&>) {
|
|
40
|
+
if (isFull()) [[unlikely]] {
|
|
49
41
|
return false;
|
|
50
42
|
}
|
|
51
43
|
new (&buffer_[tailIndex_]) T(std::forward<U>(value));
|
|
@@ -58,8 +50,8 @@ class RingBiDirectionalBuffer {
|
|
|
58
50
|
/// @param value The value to push.
|
|
59
51
|
/// @return True if the value was pushed successfully, false if the buffer is full.
|
|
60
52
|
template <typename U>
|
|
61
|
-
bool pushFront(U&&
|
|
62
|
-
if (isFull()) [[
|
|
53
|
+
bool pushFront(U &&value) noexcept(std::is_nothrow_constructible_v<T, U &&>) {
|
|
54
|
+
if (isFull()) [[unlikely]] {
|
|
63
55
|
return false;
|
|
64
56
|
}
|
|
65
57
|
headIndex_ = prevIndex(headIndex_);
|
|
@@ -70,8 +62,9 @@ class RingBiDirectionalBuffer {
|
|
|
70
62
|
/// @brief Pop a value from the front of the buffer.
|
|
71
63
|
/// @param out The value popped from the buffer.
|
|
72
64
|
/// @return True if the value was popped successfully, false if the buffer is empty.
|
|
73
|
-
bool popFront(T&
|
|
74
|
-
|
|
65
|
+
bool popFront(T &out) noexcept(
|
|
66
|
+
std::is_nothrow_move_constructible_v<T> && std::is_nothrow_destructible_v<T>) {
|
|
67
|
+
if (isEmpty()) [[unlikely]] {
|
|
75
68
|
return false;
|
|
76
69
|
}
|
|
77
70
|
out = std::move(buffer_[headIndex_]);
|
|
@@ -83,7 +76,7 @@ class RingBiDirectionalBuffer {
|
|
|
83
76
|
/// @brief Pop a value from the front of the buffer.
|
|
84
77
|
/// @return True if the value was popped successfully, false if the buffer is empty.
|
|
85
78
|
bool popFront() noexcept(std::is_nothrow_destructible_v<T>) {
|
|
86
|
-
if (isEmpty()) [[
|
|
79
|
+
if (isEmpty()) [[unlikely]] {
|
|
87
80
|
return false;
|
|
88
81
|
}
|
|
89
82
|
buffer_[headIndex_].~T();
|
|
@@ -94,8 +87,9 @@ class RingBiDirectionalBuffer {
|
|
|
94
87
|
/// @brief Pop a value from the back of the buffer.
|
|
95
88
|
/// @param out The value popped from the buffer.
|
|
96
89
|
/// @return True if the value was popped successfully, false if the buffer is empty.
|
|
97
|
-
bool popBack(T&
|
|
98
|
-
|
|
90
|
+
bool popBack(T &out) noexcept(
|
|
91
|
+
std::is_nothrow_move_constructible_v<T> && std::is_nothrow_destructible_v<T>) {
|
|
92
|
+
if (isEmpty()) [[unlikely]] {
|
|
99
93
|
return false;
|
|
100
94
|
}
|
|
101
95
|
tailIndex_ = prevIndex(tailIndex_);
|
|
@@ -107,7 +101,7 @@ class RingBiDirectionalBuffer {
|
|
|
107
101
|
/// @brief Pop a value from the back of the buffer.
|
|
108
102
|
/// @return True if the value was popped successfully, false if the buffer is empty.
|
|
109
103
|
bool popBack() noexcept(std::is_nothrow_destructible_v<T>) {
|
|
110
|
-
if (isEmpty()) [[
|
|
104
|
+
if (isEmpty()) [[unlikely]] {
|
|
111
105
|
return false;
|
|
112
106
|
}
|
|
113
107
|
tailIndex_ = prevIndex(tailIndex_);
|
|
@@ -117,25 +111,25 @@ class RingBiDirectionalBuffer {
|
|
|
117
111
|
|
|
118
112
|
/// @brief Peek at the front of the buffer.
|
|
119
113
|
/// @return A const reference to the front element of the buffer.
|
|
120
|
-
const inline T&
|
|
114
|
+
const inline T &peekFront() const noexcept {
|
|
121
115
|
return buffer_[headIndex_];
|
|
122
116
|
}
|
|
123
117
|
|
|
124
118
|
/// @brief Peek at the back of the buffer.
|
|
125
119
|
/// @return A const reference to the back element of the buffer.
|
|
126
|
-
const inline T&
|
|
120
|
+
const inline T &peekBack() const noexcept {
|
|
127
121
|
return buffer_[prevIndex(tailIndex_)];
|
|
128
122
|
}
|
|
129
123
|
|
|
130
124
|
/// @brief Peek at the front of the buffer.
|
|
131
125
|
/// @return A mutable reference to the front element of the buffer.
|
|
132
|
-
inline T&
|
|
126
|
+
inline T &peekFrontMut() noexcept {
|
|
133
127
|
return buffer_[headIndex_];
|
|
134
128
|
}
|
|
135
129
|
|
|
136
130
|
/// @brief Peek at the back of the buffer.
|
|
137
131
|
/// @return A mutable reference to the back element of the buffer.
|
|
138
|
-
inline T&
|
|
132
|
+
inline T &peekBackMut() noexcept {
|
|
139
133
|
return buffer_[prevIndex(tailIndex_)];
|
|
140
134
|
}
|
|
141
135
|
|
|
@@ -196,4 +190,4 @@ class RingBiDirectionalBuffer {
|
|
|
196
190
|
}
|
|
197
191
|
};
|
|
198
192
|
|
|
199
|
-
};
|
|
193
|
+
}; // namespace audioapi
|