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