1 #ifndef ALCONTEXT_H
2 #define ALCONTEXT_H
3 
4 #include <array>
5 #include <atomic>
6 #include <cstddef>
7 #include <cstdint>
8 #include <memory>
9 #include <mutex>
10 #include <thread>
11 #include <utility>
12 
13 #include "AL/al.h"
14 #include "AL/alc.h"
15 
16 #include "al/listener.h"
17 #include "almalloc.h"
18 #include "alnumeric.h"
19 #include "alu.h"
20 #include "atomic.h"
21 #include "inprogext.h"
22 #include "intrusive_ptr.h"
23 #include "threads.h"
24 #include "vecmat.h"
25 #include "vector.h"
26 
27 struct ALeffectslot;
28 struct ALsource;
29 struct EffectSlot;
30 struct EffectSlotProps;
31 struct RingBuffer;
32 struct Voice;
33 struct VoiceChange;
34 struct VoicePropsItem;
35 
36 
37 enum class DistanceModel : unsigned char {
38     Disable,
39     Inverse, InverseClamped,
40     Linear, LinearClamped,
41     Exponent, ExponentClamped,
42 
43     Default = InverseClamped
44 };
45 
46 
47 struct WetBuffer {
48     bool mInUse;
49     al::FlexArray<FloatBufferLine, 16> mBuffer;
50 
WetBufferWetBuffer51     WetBuffer(size_t count) : mBuffer{count} { }
52 
53     DEF_FAM_NEWDEL(WetBuffer, mBuffer)
54 };
55 using WetBufferPtr = std::unique_ptr<WetBuffer>;
56 
57 
58 struct ContextProps {
59     float DopplerFactor;
60     float DopplerVelocity;
61     float SpeedOfSound;
62     bool SourceDistanceModel;
63     DistanceModel mDistanceModel;
64 
65     std::atomic<ContextProps*> next;
66 
67     DEF_NEWDEL(ContextProps)
68 };
69 
70 struct ListenerProps {
71     std::array<float,3> Position;
72     std::array<float,3> Velocity;
73     std::array<float,3> OrientAt;
74     std::array<float,3> OrientUp;
75     float Gain;
76     float MetersPerUnit;
77 
78     std::atomic<ListenerProps*> next;
79 
80     DEF_NEWDEL(ListenerProps)
81 };
82 
83 struct ContextParams {
84     /* Pointer to the most recent property values that are awaiting an update. */
85     std::atomic<ContextProps*> ContextUpdate{nullptr};
86     std::atomic<ListenerProps*> ListenerUpdate{nullptr};
87 
88     alu::Matrix Matrix{alu::Matrix::Identity()};
89     alu::Vector Velocity{};
90 
91     float Gain{1.0f};
92     float MetersPerUnit{1.0f};
93 
94     float DopplerFactor{1.0f};
95     float SpeedOfSound{343.3f}; /* in units per sec! */
96 
97     bool SourceDistanceModel{false};
98     DistanceModel mDistanceModel{};
99 };
100 
101 
102 struct SourceSubList {
103     uint64_t FreeMask{~0_u64};
104     ALsource *Sources{nullptr}; /* 64 */
105 
106     SourceSubList() noexcept = default;
107     SourceSubList(const SourceSubList&) = delete;
SourceSubListSourceSubList108     SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
109     { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
110     ~SourceSubList();
111 
112     SourceSubList& operator=(const SourceSubList&) = delete;
113     SourceSubList& operator=(SourceSubList&& rhs) noexcept
114     { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
115 };
116 
117 struct EffectSlotSubList {
118     uint64_t FreeMask{~0_u64};
119     ALeffectslot *EffectSlots{nullptr}; /* 64 */
120 
121     EffectSlotSubList() noexcept = default;
122     EffectSlotSubList(const EffectSlotSubList&) = delete;
EffectSlotSubListEffectSlotSubList123     EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
124       : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
125     { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
126     ~EffectSlotSubList();
127 
128     EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
129     EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
130     { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
131 };
132 
133 struct ALCcontext : public al::intrusive_ref<ALCcontext> {
134     const al::intrusive_ptr<ALCdevice> mDevice;
135 
136     /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
137      * indicates if updates are currently happening).
138      */
139     RefCount mUpdateCount{0u};
140     std::atomic<bool> mHoldUpdates{false};
141 
142     float mGainBoost{1.0f};
143 
144     /* Linked lists of unused property containers, free to use for future
145      * updates.
146      */
147     std::atomic<ContextProps*> mFreeContextProps{nullptr};
148     std::atomic<ListenerProps*> mFreeListenerProps{nullptr};
149     std::atomic<VoicePropsItem*> mFreeVoiceProps{nullptr};
150     std::atomic<EffectSlotProps*> mFreeEffectslotProps{nullptr};
151 
152     /* The voice change tail is the beginning of the "free" elements, up to and
153      * *excluding* the current. If tail==current, there's no free elements and
154      * new ones need to be allocated. The current voice change is the element
155      * last processed, and any after are pending.
156      */
157     VoiceChange *mVoiceChangeTail{};
158     std::atomic<VoiceChange*> mCurrentVoiceChange{};
159 
160     void allocVoiceChanges(size_t addcount);
161 
162 
163     ContextParams mParams;
164 
165     using VoiceArray = al::FlexArray<Voice*>;
166     std::atomic<VoiceArray*> mVoices{};
167     std::atomic<size_t> mActiveVoiceCount{};
168 
169     void allocVoices(size_t addcount);
getVoicesSpanALCcontext170     al::span<Voice*> getVoicesSpan() const noexcept
171     {
172         return {mVoices.load(std::memory_order_relaxed)->data(),
173             mActiveVoiceCount.load(std::memory_order_relaxed)};
174     }
getVoicesSpanAcquiredALCcontext175     al::span<Voice*> getVoicesSpanAcquired() const noexcept
176     {
177         return {mVoices.load(std::memory_order_acquire)->data(),
178             mActiveVoiceCount.load(std::memory_order_acquire)};
179     }
180 
181 
182     using EffectSlotArray = al::FlexArray<EffectSlot*>;
183     std::atomic<EffectSlotArray*> mActiveAuxSlots{nullptr};
184 
185     std::thread mEventThread;
186     al::semaphore mEventSem;
187     std::unique_ptr<RingBuffer> mAsyncEvents;
188     std::atomic<uint> mEnabledEvts{0u};
189 
190     /* Asynchronous voice change actions are processed as a linked list of
191      * VoiceChange objects by the mixer, which is atomically appended to.
192      * However, to avoid allocating each object individually, they're allocated
193      * in clusters that are stored in a vector for easy automatic cleanup.
194      */
195     using VoiceChangeCluster = std::unique_ptr<VoiceChange[]>;
196     al::vector<VoiceChangeCluster> mVoiceChangeClusters;
197 
198     using VoiceCluster = std::unique_ptr<Voice[]>;
199     al::vector<VoiceCluster> mVoiceClusters;
200 
201     /* Wet buffers used by effect slots. */
202     al::vector<WetBufferPtr> mWetBuffers;
203 
204 
205     std::atomic_flag mPropsClean;
206     std::atomic<bool> mDeferUpdates{false};
207 
208     std::mutex mPropLock;
209 
210     std::atomic<ALenum> mLastError{AL_NO_ERROR};
211 
212     DistanceModel mDistanceModel{DistanceModel::Default};
213     bool mSourceDistanceModel{false};
214 
215     float mDopplerFactor{1.0f};
216     float mDopplerVelocity{1.0f};
217     float mSpeedOfSound{SpeedOfSoundMetersPerSec};
218 
219     std::mutex mEventCbLock;
220     ALEVENTPROCSOFT mEventCb{};
221     void *mEventParam{nullptr};
222 
223     ALlistener mListener{};
224 
225     al::vector<SourceSubList> mSourceList;
226     ALuint mNumSources{0};
227     std::mutex mSourceLock;
228 
229     al::vector<EffectSlotSubList> mEffectSlotList;
230     ALuint mNumEffectSlots{0u};
231     std::mutex mEffectSlotLock;
232 
233     /* Default effect slot */
234     std::unique_ptr<ALeffectslot> mDefaultSlot;
235 
236     const char *mExtensionList{nullptr};
237 
238 
239     ALCcontext(al::intrusive_ptr<ALCdevice> device);
240     ALCcontext(const ALCcontext&) = delete;
241     ALCcontext& operator=(const ALCcontext&) = delete;
242     ~ALCcontext();
243 
244     void init();
245     /**
246      * Removes the context from its device and removes it from being current on
247      * the running thread or globally. Returns true if other contexts still
248      * exist on the device.
249      */
250     bool deinit();
251 
252     /**
253      * Defers/suspends updates for the given context's listener and sources.
254      * This does *NOT* stop mixing, but rather prevents certain property
255      * changes from taking effect.
256      */
deferUpdatesALCcontext257     void deferUpdates() noexcept { mDeferUpdates.exchange(true, std::memory_order_acq_rel); }
258 
259     /** Resumes update processing after being deferred. */
260     void processUpdates();
261 
262     [[gnu::format(printf,3,4)]] void setError(ALenum errorCode, const char *msg, ...);
263 
264     DEF_NEWDEL(ALCcontext)
265 };
266 
267 #define SETERR_RETURN(ctx, err, retval, ...) do {                             \
268     (ctx)->setError((err), __VA_ARGS__);                                      \
269     return retval;                                                            \
270 } while(0)
271 
272 
273 using ContextRef = al::intrusive_ptr<ALCcontext>;
274 
275 ContextRef GetContextRef(void);
276 
277 void UpdateContextProps(ALCcontext *context);
278 
279 
280 extern bool TrapALError;
281 
282 #endif /* ALCONTEXT_H */
283