123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- #ifndef ALC_CONTEXT_H
- #define ALC_CONTEXT_H
- #include <array>
- #include <atomic>
- #include <cstdint>
- #include <deque>
- #include <memory>
- #include <mutex>
- #include <string>
- #include <string_view>
- #include <unordered_map>
- #include <utility>
- #include <vector>
- #include "AL/al.h"
- #include "AL/alc.h"
- #include "AL/alext.h"
- #include "al/listener.h"
- #include "almalloc.h"
- #include "alnumeric.h"
- #include "althreads.h"
- #include "atomic.h"
- #include "core/context.h"
- #include "inprogext.h"
- #include "intrusive_ptr.h"
- #ifdef ALSOFT_EAX
- #include "al/eax/call.h"
- #include "al/eax/exception.h"
- #include "al/eax/fx_slot_index.h"
- #include "al/eax/fx_slots.h"
- #include "al/eax/utils.h"
- #endif // ALSOFT_EAX
- struct ALeffect;
- struct ALeffectslot;
- struct ALsource;
- struct DebugGroup;
- struct EffectSlotSubList;
- struct SourceSubList;
- enum class DebugSource : std::uint8_t;
- enum class DebugType : std::uint8_t;
- enum class DebugSeverity : std::uint8_t;
- using uint = unsigned int;
- enum ContextFlags {
- DebugBit = 0, /* ALC_CONTEXT_DEBUG_BIT_EXT */
- };
- using ContextFlagBitset = std::bitset<sizeof(ALuint)*8>;
- struct DebugLogEntry {
- const DebugSource mSource;
- const DebugType mType;
- const DebugSeverity mSeverity;
- const uint mId;
- std::string mMessage;
- template<typename T>
- DebugLogEntry(DebugSource source, DebugType type, uint id, DebugSeverity severity, T&& message)
- : mSource{source}, mType{type}, mSeverity{severity}, mId{id}
- , mMessage{std::forward<T>(message)}
- { }
- DebugLogEntry(const DebugLogEntry&) = default;
- DebugLogEntry(DebugLogEntry&&) = default;
- };
- struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
- const al::intrusive_ptr<ALCdevice> mALDevice;
- bool mPropsDirty{true};
- bool mDeferUpdates{false};
- std::mutex mPropLock;
- al::tss<ALenum> mLastThreadError{AL_NO_ERROR};
- const ContextFlagBitset mContextFlags;
- std::atomic<bool> mDebugEnabled{false};
- DistanceModel mDistanceModel{DistanceModel::Default};
- bool mSourceDistanceModel{false};
- float mDopplerFactor{1.0f};
- float mDopplerVelocity{1.0f};
- float mSpeedOfSound{SpeedOfSoundMetersPerSec};
- float mAirAbsorptionGainHF{AirAbsorbGainHF};
- std::mutex mEventCbLock;
- ALEVENTPROCSOFT mEventCb{};
- void *mEventParam{nullptr};
- std::mutex mDebugCbLock;
- ALDEBUGPROCEXT mDebugCb{};
- void *mDebugParam{nullptr};
- std::vector<DebugGroup> mDebugGroups;
- std::deque<DebugLogEntry> mDebugLog;
- ALlistener mListener{};
- std::vector<SourceSubList> mSourceList;
- ALuint mNumSources{0};
- std::mutex mSourceLock;
- std::vector<EffectSlotSubList> mEffectSlotList;
- ALuint mNumEffectSlots{0u};
- std::mutex mEffectSlotLock;
- /* Default effect slot */
- std::unique_ptr<ALeffectslot> mDefaultSlot;
- std::vector<std::string_view> mExtensions;
- std::string mExtensionsString{};
- std::unordered_map<ALuint,std::string> mSourceNames;
- std::unordered_map<ALuint,std::string> mEffectSlotNames;
- ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags);
- ALCcontext(const ALCcontext&) = delete;
- ALCcontext& operator=(const ALCcontext&) = delete;
- ~ALCcontext();
- void init();
- /**
- * Removes the context from its device and removes it from being current on
- * the running thread or globally. Stops device playback if this was the
- * last context on its device.
- */
- void deinit();
- /**
- * Defers/suspends updates for the given context's listener and sources.
- * This does *NOT* stop mixing, but rather prevents certain property
- * changes from taking effect. mPropLock must be held when called.
- */
- void deferUpdates() noexcept { mDeferUpdates = true; }
- /**
- * Resumes update processing after being deferred. mPropLock must be held
- * when called.
- */
- void processUpdates()
- {
- if(std::exchange(mDeferUpdates, false))
- applyAllUpdates();
- }
- /**
- * Applies all pending updates for the context, listener, effect slots, and
- * sources.
- */
- void applyAllUpdates();
- #ifdef __MINGW32__
- [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
- #else
- [[gnu::format(printf, 3, 4)]]
- #endif
- void setError(ALenum errorCode, const char *msg, ...);
- void sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
- DebugType type, ALuint id, DebugSeverity severity, std::string_view message);
- void debugMessage(DebugSource source, DebugType type, ALuint id, DebugSeverity severity,
- std::string_view message)
- {
- if(!mDebugEnabled.load(std::memory_order_relaxed)) LIKELY
- return;
- std::unique_lock<std::mutex> debuglock{mDebugCbLock};
- sendDebugMessage(debuglock, source, type, id, severity, message);
- }
- /* Process-wide current context */
- static std::atomic<bool> sGlobalContextLock;
- static std::atomic<ALCcontext*> sGlobalContext;
- private:
- /* Thread-local current context. */
- static inline thread_local ALCcontext *sLocalContext{};
- /* Thread-local context handling. This handles attempting to release the
- * context which may have been left current when the thread is destroyed.
- */
- class ThreadCtx {
- public:
- ~ThreadCtx();
- /* NOLINTBEGIN(readability-convert-member-functions-to-static)
- * This should be non-static to invoke construction of the thread-local
- * sThreadContext, so that it's destructor gets run at thread exit to
- * clear sLocalContext (which isn't a member variable to make read
- * access efficient).
- */
- void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
- /* NOLINTEND(readability-convert-member-functions-to-static) */
- };
- static thread_local ThreadCtx sThreadContext;
- public:
- static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
- static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
- /* Default effect that applies to sources that don't have an effect on send 0. */
- static ALeffect sDefaultEffect;
- #ifdef ALSOFT_EAX
- bool hasEax() const noexcept { return mEaxIsInitialized; }
- bool eaxIsCapable() const noexcept;
- void eaxUninitialize() noexcept;
- ALenum eax_eax_set(
- const GUID* property_set_id,
- ALuint property_id,
- ALuint property_source_id,
- ALvoid* property_value,
- ALuint property_value_size);
- ALenum eax_eax_get(
- const GUID* property_set_id,
- ALuint property_id,
- ALuint property_source_id,
- ALvoid* property_value,
- ALuint property_value_size);
- void eaxSetLastError() noexcept;
- EaxFxSlotIndex eaxGetPrimaryFxSlotIndex() const noexcept
- { return mEaxPrimaryFxSlotIndex; }
- const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const
- { return mEaxFxSlots.get(fx_slot_index); }
- ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index)
- { return mEaxFxSlots.get(fx_slot_index); }
- bool eaxNeedsCommit() const noexcept { return mEaxNeedsCommit; }
- void eaxCommit();
- void eaxCommitFxSlots()
- { mEaxFxSlots.commit(); }
- private:
- static constexpr auto eax_primary_fx_slot_id_dirty_bit = EaxDirtyFlags{1} << 0;
- static constexpr auto eax_distance_factor_dirty_bit = EaxDirtyFlags{1} << 1;
- static constexpr auto eax_air_absorption_hf_dirty_bit = EaxDirtyFlags{1} << 2;
- static constexpr auto eax_hf_reference_dirty_bit = EaxDirtyFlags{1} << 3;
- static constexpr auto eax_macro_fx_factor_dirty_bit = EaxDirtyFlags{1} << 4;
- using Eax4Props = EAX40CONTEXTPROPERTIES;
- struct Eax4State {
- Eax4Props i; // Immediate.
- Eax4Props d; // Deferred.
- };
- using Eax5Props = EAX50CONTEXTPROPERTIES;
- struct Eax5State {
- Eax5Props i; // Immediate.
- Eax5Props d; // Deferred.
- };
- class ContextException : public EaxException
- {
- public:
- explicit ContextException(const char* message)
- : EaxException{"EAX_CONTEXT", message}
- {}
- };
- struct Eax4PrimaryFxSlotIdValidator {
- void operator()(const GUID& guidPrimaryFXSlotID) const
- {
- if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
- guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 &&
- guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 &&
- guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 &&
- guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot3)
- {
- eax_fail_unknown_primary_fx_slot_id();
- }
- }
- };
- struct Eax4DistanceFactorValidator {
- void operator()(float flDistanceFactor) const
- {
- eax_validate_range<ContextException>(
- "Distance Factor",
- flDistanceFactor,
- EAXCONTEXT_MINDISTANCEFACTOR,
- EAXCONTEXT_MAXDISTANCEFACTOR);
- }
- };
- struct Eax4AirAbsorptionHfValidator {
- void operator()(float flAirAbsorptionHF) const
- {
- eax_validate_range<ContextException>(
- "Air Absorption HF",
- flAirAbsorptionHF,
- EAXCONTEXT_MINAIRABSORPTIONHF,
- EAXCONTEXT_MAXAIRABSORPTIONHF);
- }
- };
- struct Eax4HfReferenceValidator {
- void operator()(float flHFReference) const
- {
- eax_validate_range<ContextException>(
- "HF Reference",
- flHFReference,
- EAXCONTEXT_MINHFREFERENCE,
- EAXCONTEXT_MAXHFREFERENCE);
- }
- };
- struct Eax4AllValidator {
- void operator()(const EAX40CONTEXTPROPERTIES& all) const
- {
- Eax4PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
- Eax4DistanceFactorValidator{}(all.flDistanceFactor);
- Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
- Eax4HfReferenceValidator{}(all.flHFReference);
- }
- };
- struct Eax5PrimaryFxSlotIdValidator {
- void operator()(const GUID& guidPrimaryFXSlotID) const
- {
- if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
- guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 &&
- guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 &&
- guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 &&
- guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot3)
- {
- eax_fail_unknown_primary_fx_slot_id();
- }
- }
- };
- struct Eax5MacroFxFactorValidator {
- void operator()(float flMacroFXFactor) const
- {
- eax_validate_range<ContextException>(
- "Macro FX Factor",
- flMacroFXFactor,
- EAXCONTEXT_MINMACROFXFACTOR,
- EAXCONTEXT_MAXMACROFXFACTOR);
- }
- };
- struct Eax5AllValidator {
- void operator()(const EAX50CONTEXTPROPERTIES& all) const
- {
- Eax5PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
- Eax4DistanceFactorValidator{}(all.flDistanceFactor);
- Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
- Eax4HfReferenceValidator{}(all.flHFReference);
- Eax5MacroFxFactorValidator{}(all.flMacroFXFactor);
- }
- };
- struct Eax5EaxVersionValidator {
- void operator()(unsigned long ulEAXVersion) const
- {
- eax_validate_range<ContextException>(
- "EAX version",
- ulEAXVersion,
- EAXCONTEXT_MINEAXSESSION,
- EAXCONTEXT_MAXEAXSESSION);
- }
- };
- struct Eax5MaxActiveSendsValidator {
- void operator()(unsigned long ulMaxActiveSends) const
- {
- eax_validate_range<ContextException>(
- "Max Active Sends",
- ulMaxActiveSends,
- EAXCONTEXT_MINMAXACTIVESENDS,
- EAXCONTEXT_MAXMAXACTIVESENDS);
- }
- };
- struct Eax5SessionAllValidator {
- void operator()(const EAXSESSIONPROPERTIES& all) const
- {
- Eax5EaxVersionValidator{}(all.ulEAXVersion);
- Eax5MaxActiveSendsValidator{}(all.ulMaxActiveSends);
- }
- };
- struct Eax5SpeakerConfigValidator {
- void operator()(unsigned long ulSpeakerConfig) const
- {
- eax_validate_range<ContextException>(
- "Speaker Config",
- ulSpeakerConfig,
- EAXCONTEXT_MINSPEAKERCONFIG,
- EAXCONTEXT_MAXSPEAKERCONFIG);
- }
- };
- bool mEaxIsInitialized{};
- bool mEaxIsTried{};
- long mEaxLastError{};
- unsigned long mEaxSpeakerConfig{};
- EaxFxSlotIndex mEaxPrimaryFxSlotIndex{};
- EaxFxSlots mEaxFxSlots{};
- int mEaxVersion{}; // Current EAX version.
- bool mEaxNeedsCommit{};
- EaxDirtyFlags mEaxDf{}; // Dirty flags for the current EAX version.
- Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
- Eax4State mEax4{}; // EAX4 state.
- Eax5State mEax5{}; // EAX5 state.
- Eax5Props mEax{}; // Current EAX state.
- EAXSESSIONPROPERTIES mEaxSession{};
- [[noreturn]] static void eax_fail(const char* message);
- [[noreturn]] static void eax_fail_unknown_property_set_id();
- [[noreturn]] static void eax_fail_unknown_primary_fx_slot_id();
- [[noreturn]] static void eax_fail_unknown_property_id();
- [[noreturn]] static void eax_fail_unknown_version();
- // Gets a value from EAX call,
- // validates it,
- // and updates the current value.
- template<typename TValidator, typename TProperty>
- static void eax_set(const EaxCall& call, TProperty& property)
- {
- const auto& value = call.get_value<ContextException, const TProperty>();
- TValidator{}(value);
- property = value;
- }
- // Gets a new value from EAX call,
- // validates it,
- // updates the deferred value,
- // updates a dirty flag.
- template<
- typename TValidator,
- EaxDirtyFlags TDirtyBit,
- typename TMemberResult,
- typename TProps,
- typename TState>
- void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member)
- {
- const auto& src = call.get_value<ContextException, const TMemberResult>();
- TValidator{}(src);
- const auto& dst_i = state.i.*member;
- auto& dst_d = state.d.*member;
- dst_d = src;
- if(dst_i != dst_d)
- mEaxDf |= TDirtyBit;
- }
- template<
- EaxDirtyFlags TDirtyBit,
- typename TMemberResult,
- typename TProps,
- typename TState>
- void eax_context_commit_property(TState& state, EaxDirtyFlags& dst_df,
- TMemberResult TProps::*member) noexcept
- {
- if((mEaxDf & TDirtyBit) != EaxDirtyFlags{})
- {
- dst_df |= TDirtyBit;
- const auto& src_d = state.d.*member;
- state.i.*member = src_d;
- mEax.*member = src_d;
- }
- }
- void eax_initialize_extensions();
- void eax_initialize();
- bool eax_has_no_default_effect_slot() const noexcept;
- void eax_ensure_no_default_effect_slot() const;
- bool eax_has_enough_aux_sends() const noexcept;
- void eax_ensure_enough_aux_sends() const;
- void eax_ensure_compatibility();
- unsigned long eax_detect_speaker_configuration() const;
- void eax_update_speaker_configuration();
- void eax_set_last_error_defaults() noexcept;
- void eax_session_set_defaults() noexcept;
- static void eax4_context_set_defaults(Eax4Props& props) noexcept;
- static void eax4_context_set_defaults(Eax4State& state) noexcept;
- static void eax5_context_set_defaults(Eax5Props& props) noexcept;
- static void eax5_context_set_defaults(Eax5State& state) noexcept;
- void eax_context_set_defaults();
- void eax_set_defaults();
- void eax_dispatch_fx_slot(const EaxCall& call);
- void eax_dispatch_source(const EaxCall& call);
- void eax_get_misc(const EaxCall& call);
- void eax4_get(const EaxCall& call, const Eax4Props& props);
- void eax5_get(const EaxCall& call, const Eax5Props& props);
- void eax_get(const EaxCall& call);
- void eax_context_commit_primary_fx_slot_id();
- void eax_context_commit_distance_factor();
- void eax_context_commit_air_absorbtion_hf();
- void eax_context_commit_hf_reference();
- void eax_context_commit_macro_fx_factor();
- void eax_initialize_fx_slots();
- void eax_update_sources();
- void eax_set_misc(const EaxCall& call);
- void eax4_defer_all(const EaxCall& call, Eax4State& state);
- void eax4_defer(const EaxCall& call, Eax4State& state);
- void eax5_defer_all(const EaxCall& call, Eax5State& state);
- void eax5_defer(const EaxCall& call, Eax5State& state);
- void eax_set(const EaxCall& call);
- void eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df);
- void eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df);
- void eax_context_commit();
- #endif // ALSOFT_EAX
- };
- using ContextRef = al::intrusive_ptr<ALCcontext>;
- ContextRef GetContextRef() noexcept;
- void UpdateContextProps(ALCcontext *context);
- inline bool TrapALError{false};
- #ifdef ALSOFT_EAX
- auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
- ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
- auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id,
- ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
- #endif // ALSOFT_EAX
- #endif /* ALC_CONTEXT_H */
|