15 #include "al/listener.h"
17 #include "alnumeric.h"
20 #include "inprogext.h"
21 #include "intrusive_ptr.h"
27 struct ALeffectslotProps
;
32 enum class DistanceModel
{
33 InverseClamped
= AL_INVERSE_DISTANCE_CLAMPED
,
34 LinearClamped
= AL_LINEAR_DISTANCE_CLAMPED
,
35 ExponentClamped
= AL_EXPONENT_DISTANCE_CLAMPED
,
36 Inverse
= AL_INVERSE_DISTANCE
,
37 Linear
= AL_LINEAR_DISTANCE
,
38 Exponent
= AL_EXPONENT_DISTANCE
,
41 Default
= InverseClamped
45 struct ALcontextProps
{
47 float DopplerVelocity
;
49 bool SourceDistanceModel
;
50 DistanceModel mDistanceModel
;
52 std::atomic
<ALcontextProps
*> next
;
54 DEF_NEWDEL(ALcontextProps
)
59 Voice
*mOldVoice
{nullptr};
60 Voice
*mVoice
{nullptr};
64 std::atomic
<VoiceChange
*> mNext
{nullptr};
66 DEF_NEWDEL(VoiceChange
)
70 struct SourceSubList
{
71 uint64_t FreeMask
{~0_u64
};
72 ALsource
*Sources
{nullptr}; /* 64 */
74 SourceSubList() noexcept
= default;
75 SourceSubList(const SourceSubList
&) = delete;
76 SourceSubList(SourceSubList
&& rhs
) noexcept
: FreeMask
{rhs
.FreeMask
}, Sources
{rhs
.Sources
}
77 { rhs
.FreeMask
= ~0_u64
; rhs
.Sources
= nullptr; }
80 SourceSubList
& operator=(const SourceSubList
&) = delete;
81 SourceSubList
& operator=(SourceSubList
&& rhs
) noexcept
82 { std::swap(FreeMask
, rhs
.FreeMask
); std::swap(Sources
, rhs
.Sources
); return *this; }
85 struct EffectSlotSubList
{
86 uint64_t FreeMask
{~0_u64
};
87 ALeffectslot
*EffectSlots
{nullptr}; /* 64 */
89 EffectSlotSubList() noexcept
= default;
90 EffectSlotSubList(const EffectSlotSubList
&) = delete;
91 EffectSlotSubList(EffectSlotSubList
&& rhs
) noexcept
92 : FreeMask
{rhs
.FreeMask
}, EffectSlots
{rhs
.EffectSlots
}
93 { rhs
.FreeMask
= ~0_u64
; rhs
.EffectSlots
= nullptr; }
96 EffectSlotSubList
& operator=(const EffectSlotSubList
&) = delete;
97 EffectSlotSubList
& operator=(EffectSlotSubList
&& rhs
) noexcept
98 { std::swap(FreeMask
, rhs
.FreeMask
); std::swap(EffectSlots
, rhs
.EffectSlots
); return *this; }
101 struct ALCcontext
: public al::intrusive_ref
<ALCcontext
> {
102 al::vector
<SourceSubList
> mSourceList
;
103 ALuint mNumSources
{0};
104 std::mutex mSourceLock
;
106 al::vector
<EffectSlotSubList
> mEffectSlotList
;
107 ALuint mNumEffectSlots
{0u};
108 std::mutex mEffectSlotLock
;
110 std::atomic
<ALenum
> mLastError
{AL_NO_ERROR
};
112 DistanceModel mDistanceModel
{DistanceModel::Default
};
113 bool mSourceDistanceModel
{false};
115 float mDopplerFactor
{1.0f
};
116 float mDopplerVelocity
{1.0f
};
117 float mSpeedOfSound
{SPEEDOFSOUNDMETRESPERSEC
};
119 std::atomic_flag mPropsClean
;
120 std::atomic
<bool> mDeferUpdates
{false};
122 std::mutex mPropLock
;
124 /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
125 * indicates if updates are currently happening).
127 RefCount mUpdateCount
{0u};
128 std::atomic
<bool> mHoldUpdates
{false};
130 float mGainBoost
{1.0f
};
132 std::atomic
<ALcontextProps
*> mUpdate
{nullptr};
134 /* Linked lists of unused property containers, free to use for future
137 std::atomic
<ALcontextProps
*> mFreeContextProps
{nullptr};
138 std::atomic
<ALlistenerProps
*> mFreeListenerProps
{nullptr};
139 std::atomic
<VoicePropsItem
*> mFreeVoiceProps
{nullptr};
140 std::atomic
<ALeffectslotProps
*> mFreeEffectslotProps
{nullptr};
142 /* Asynchronous voice change actions are processed as a linked list of
143 * VoiceChange objects by the mixer, which is atomically appended to.
144 * However, to avoid allocating each object individually, they're allocated
145 * in clusters that are stored in a vector for easy automatic cleanup.
147 using VoiceChangeCluster
= std::unique_ptr
<VoiceChange
[]>;
148 al::vector
<VoiceChangeCluster
> mVoiceChangeClusters
;
150 /* The voice change tail is the beginning of the "free" elements, up to and
151 * *excluding* the current. If tail==current, there's no free elements and
152 * new ones need to be allocated. The current voice change is the element
153 * last processed, and any after are pending.
155 VoiceChange
*mVoiceChangeTail
{};
156 std::atomic
<VoiceChange
*> mCurrentVoiceChange
{};
158 void allocVoiceChanges(size_t addcount
);
161 using VoiceCluster
= std::unique_ptr
<Voice
[]>;
162 al::vector
<VoiceCluster
> mVoiceClusters
;
164 using VoiceArray
= al::FlexArray
<Voice
*>;
165 std::atomic
<VoiceArray
*> mVoices
{};
166 std::atomic
<size_t> mActiveVoiceCount
{};
168 void allocVoices(size_t addcount
);
169 al::span
<Voice
*> getVoicesSpan() const noexcept
171 return {mVoices
.load(std::memory_order_relaxed
)->data(),
172 mActiveVoiceCount
.load(std::memory_order_relaxed
)};
174 al::span
<Voice
*> getVoicesSpanAcquired() const noexcept
176 return {mVoices
.load(std::memory_order_acquire
)->data(),
177 mActiveVoiceCount
.load(std::memory_order_acquire
)};
181 using ALeffectslotArray
= al::FlexArray
<ALeffectslot
*>;
182 std::atomic
<ALeffectslotArray
*> mActiveAuxSlots
{nullptr};
184 std::thread mEventThread
;
185 al::semaphore mEventSem
;
186 std::unique_ptr
<RingBuffer
> mAsyncEvents
;
187 std::atomic
<ALbitfieldSOFT
> mEnabledEvts
{0u};
188 std::mutex mEventCbLock
;
189 ALEVENTPROCSOFT mEventCb
{};
190 void *mEventParam
{nullptr};
192 /* Default effect slot */
193 std::unique_ptr
<ALeffectslot
> mDefaultSlot
;
195 const al::intrusive_ptr
<ALCdevice
> mDevice
;
196 const char *mExtensionList
{nullptr};
198 ALlistener mListener
{};
201 ALCcontext(al::intrusive_ptr
<ALCdevice
> device
);
202 ALCcontext(const ALCcontext
&) = delete;
203 ALCcontext
& operator=(const ALCcontext
&) = delete;
208 * Removes the context from its device and removes it from being current on
209 * the running thread or globally. Returns true if other contexts still
210 * exist on the device.
215 * Defers/suspends updates for the given context's listener and sources.
216 * This does *NOT* stop mixing, but rather prevents certain property
217 * changes from taking effect.
219 void deferUpdates() noexcept
{ mDeferUpdates
.exchange(true, std::memory_order_acq_rel
); }
221 /** Resumes update processing after being deferred. */
222 void processUpdates();
224 [[gnu::format(printf
,3,4)]] void setError(ALenum errorCode
, const char *msg
, ...);
226 DEF_NEWDEL(ALCcontext
)
229 #define SETERR_RETURN(ctx, err, retval, ...) do { \
230 (ctx)->setError((err), __VA_ARGS__); \
235 using ContextRef
= al::intrusive_ptr
<ALCcontext
>;
237 ContextRef
GetContextRef(void);
239 void UpdateContextProps(ALCcontext
*context
);
242 extern bool TrapALError
;
244 #endif /* ALCONTEXT_H */