Use an array of 2 instead of a pair
[openal-soft.git] / core / context.h
blobe08683c4bad0849030bab96bc067a9b9f9677299
1 #ifndef CORE_CONTEXT_H
2 #define CORE_CONTEXT_H
4 #include <array>
5 #include <atomic>
6 #include <bitset>
7 #include <cstddef>
8 #include <memory>
9 #include <thread>
10 #include <vector>
12 #include "alsem.h"
13 #include "alspan.h"
14 #include "async_event.h"
15 #include "atomic.h"
16 #include "flexarray.h"
17 #include "opthelpers.h"
18 #include "vecmat.h"
20 struct DeviceBase;
21 struct EffectSlot;
22 struct EffectSlotProps;
23 struct RingBuffer;
24 struct Voice;
25 struct VoiceChange;
26 struct VoicePropsItem;
29 inline constexpr float SpeedOfSoundMetersPerSec{343.3f};
31 inline constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */
33 enum class DistanceModel : unsigned char {
34 Disable,
35 Inverse, InverseClamped,
36 Linear, LinearClamped,
37 Exponent, ExponentClamped,
39 Default = InverseClamped
43 struct ContextProps {
44 std::array<float,3> Position;
45 std::array<float,3> Velocity;
46 std::array<float,3> OrientAt;
47 std::array<float,3> OrientUp;
48 float Gain;
49 float MetersPerUnit;
50 float AirAbsorptionGainHF;
52 float DopplerFactor;
53 float DopplerVelocity;
54 float SpeedOfSound;
55 #ifdef ALSOFT_EAX
56 float DistanceFactor;
57 #endif
58 bool SourceDistanceModel;
59 DistanceModel mDistanceModel;
61 std::atomic<ContextProps*> next;
64 struct ContextParams {
65 /* Pointer to the most recent property values that are awaiting an update. */
66 std::atomic<ContextProps*> ContextUpdate{nullptr};
68 alu::Vector Position{};
69 alu::Matrix Matrix{alu::Matrix::Identity()};
70 alu::Vector Velocity{};
72 float Gain{1.0f};
73 float MetersPerUnit{1.0f};
74 float AirAbsorptionGainHF{AirAbsorbGainHF};
76 float DopplerFactor{1.0f};
77 float SpeedOfSound{SpeedOfSoundMetersPerSec}; /* in units per sec! */
79 bool SourceDistanceModel{false};
80 DistanceModel mDistanceModel{};
83 struct ContextBase {
84 DeviceBase *const mDevice;
86 /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
87 * indicates if updates are currently happening).
89 std::atomic<unsigned int> mUpdateCount{0u};
90 std::atomic<bool> mHoldUpdates{false};
91 std::atomic<bool> mStopVoicesOnDisconnect{true};
93 float mGainBoost{1.0f};
95 /* Linked lists of unused property containers, free to use for future
96 * updates.
98 std::atomic<ContextProps*> mFreeContextProps{nullptr};
99 std::atomic<VoicePropsItem*> mFreeVoiceProps{nullptr};
100 std::atomic<EffectSlotProps*> mFreeEffectSlotProps{nullptr};
102 /* The voice change tail is the beginning of the "free" elements, up to and
103 * *excluding* the current. If tail==current, there's no free elements and
104 * new ones need to be allocated. The current voice change is the element
105 * last processed, and any after are pending.
107 VoiceChange *mVoiceChangeTail{};
108 std::atomic<VoiceChange*> mCurrentVoiceChange{};
110 void allocVoiceChanges();
111 void allocVoiceProps();
112 void allocEffectSlotProps();
113 void allocContextProps();
115 ContextParams mParams;
117 using VoiceArray = al::FlexArray<Voice*>;
118 al::atomic_unique_ptr<VoiceArray> mVoices{};
119 std::atomic<size_t> mActiveVoiceCount{};
121 void allocVoices(size_t addcount);
122 [[nodiscard]] auto getVoicesSpan() const noexcept -> al::span<Voice*>
124 return {mVoices.load(std::memory_order_relaxed)->data(),
125 mActiveVoiceCount.load(std::memory_order_relaxed)};
127 [[nodiscard]] auto getVoicesSpanAcquired() const noexcept -> al::span<Voice*>
129 return {mVoices.load(std::memory_order_acquire)->data(),
130 mActiveVoiceCount.load(std::memory_order_acquire)};
134 using EffectSlotArray = al::FlexArray<EffectSlot*>;
135 /* This array is split in half. The front half is the list of activated
136 * effect slots as set by the app, and the back half is the same list but
137 * sorted to ensure later effect slots are fed by earlier ones.
139 al::atomic_unique_ptr<EffectSlotArray> mActiveAuxSlots;
141 std::thread mEventThread;
142 al::semaphore mEventSem;
143 std::unique_ptr<RingBuffer> mAsyncEvents;
144 using AsyncEventBitset = std::bitset<al::to_underlying(AsyncEnableBits::Count)>;
145 std::atomic<AsyncEventBitset> mEnabledEvts{0u};
147 /* Asynchronous voice change actions are processed as a linked list of
148 * VoiceChange objects by the mixer, which is atomically appended to.
149 * However, to avoid allocating each object individually, they're allocated
150 * in clusters that are stored in a vector for easy automatic cleanup.
152 using VoiceChangeCluster = std::unique_ptr<std::array<VoiceChange,128>>;
153 std::vector<VoiceChangeCluster> mVoiceChangeClusters;
155 using VoiceCluster = std::unique_ptr<std::array<Voice,32>>;
156 std::vector<VoiceCluster> mVoiceClusters;
158 using VoicePropsCluster = std::unique_ptr<std::array<VoicePropsItem,32>>;
159 std::vector<VoicePropsCluster> mVoicePropClusters;
162 EffectSlot *getEffectSlot();
164 using EffectSlotCluster = std::unique_ptr<std::array<EffectSlot,4>>;
165 std::vector<EffectSlotCluster> mEffectSlotClusters;
167 using EffectSlotPropsCluster = std::unique_ptr<std::array<EffectSlotProps,4>>;
168 std::vector<EffectSlotPropsCluster> mEffectSlotPropClusters;
170 /* This could be greater than 2, but there should be no way there can be
171 * more than two context property updates in use simultaneously.
173 using ContextPropsCluster = std::unique_ptr<std::array<ContextProps,2>>;
174 std::vector<ContextPropsCluster> mContextPropClusters;
177 ContextBase(DeviceBase *device);
178 ContextBase(const ContextBase&) = delete;
179 ContextBase& operator=(const ContextBase&) = delete;
180 ~ContextBase();
183 #endif /* CORE_CONTEXT_H */