openal-soft/alc/context.h

564 lines
14 KiB
C
Raw Normal View History

#ifndef ALC_CONTEXT_H
#define ALC_CONTEXT_H
#include <atomic>
#include <memory>
2019-08-01 09:21:56 -07:00
#include <mutex>
#include <stdint.h>
2019-08-01 09:21:56 -07:00
#include <utility>
2018-11-18 18:04:27 -08:00
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "al/listener.h"
2018-11-18 00:38:31 -08:00
#include "almalloc.h"
#include "alnumeric.h"
#include "atomic.h"
#include "core/context.h"
#include "intrusive_ptr.h"
#include "vector.h"
#ifdef ALSOFT_EAX
#include "al/eax_eax_call.h"
#include "al/eax_fx_slot_index.h"
#include "al/eax_fx_slots.h"
#include "al/eax_utils.h"
using EaxContextSharedDirtyFlagsValue = std::uint_least8_t;
struct EaxContextSharedDirtyFlags
{
using EaxIsBitFieldStruct = bool;
EaxContextSharedDirtyFlagsValue primary_fx_slot_id : 1;
EaxContextSharedDirtyFlagsValue air_absorption_hf : 1;
}; // EaxContextSharedDirtyFlags
using ContextDirtyFlagsValue = std::uint_least8_t;
struct ContextDirtyFlags
{
using EaxIsBitFieldStruct = bool;
ContextDirtyFlagsValue guidPrimaryFXSlotID : 1;
ContextDirtyFlagsValue flDistanceFactor : 1;
ContextDirtyFlagsValue flAirAbsorptionHF : 1;
ContextDirtyFlagsValue flHFReference : 1;
ContextDirtyFlagsValue flMacroFXFactor : 1;
}; // ContextDirtyFlags
struct EaxAlIsExtensionPresentResult
{
ALboolean is_present;
bool is_return;
}; // EaxAlIsExtensionPresentResult
#endif // ALSOFT_EAX
struct ALeffect;
struct ALeffectslot;
2019-08-01 09:21:56 -07:00
struct ALsource;
using uint = unsigned int;
2019-08-01 09:21:56 -07:00
struct SourceSubList {
uint64_t FreeMask{~0_u64};
ALsource *Sources{nullptr}; /* 64 */
SourceSubList() noexcept = default;
SourceSubList(const SourceSubList&) = delete;
SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
{ rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
~SourceSubList();
SourceSubList& operator=(const SourceSubList&) = delete;
SourceSubList& operator=(SourceSubList&& rhs) noexcept
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
};
struct EffectSlotSubList {
uint64_t FreeMask{~0_u64};
ALeffectslot *EffectSlots{nullptr}; /* 64 */
EffectSlotSubList() noexcept = default;
EffectSlotSubList(const EffectSlotSubList&) = delete;
EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
: FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
{ rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
~EffectSlotSubList();
EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
{ std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
};
struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
const al::intrusive_ptr<ALCdevice> mALDevice;
2020-12-27 01:31:52 -08:00
/* Wet buffers used by effect slots. */
al::vector<WetBufferPtr> mWetBuffers;
al::atomic_invflag mPropsDirty;
2020-12-27 01:31:52 -08:00
std::atomic<bool> mDeferUpdates{false};
std::mutex mPropLock;
std::atomic<ALenum> mLastError{AL_NO_ERROR};
DistanceModel mDistanceModel{DistanceModel::Default};
bool mSourceDistanceModel{false};
float mDopplerFactor{1.0f};
float mDopplerVelocity{1.0f};
float mSpeedOfSound{SpeedOfSoundMetersPerSec};
2019-07-30 09:05:54 -07:00
std::mutex mEventCbLock;
ALEVENTPROCSOFT mEventCb{};
void *mEventParam{nullptr};
2020-12-27 01:31:52 -08:00
ALlistener mListener{};
al::vector<SourceSubList> mSourceList;
ALuint mNumSources{0};
std::mutex mSourceLock;
al::vector<EffectSlotSubList> mEffectSlotList;
ALuint mNumEffectSlots{0u};
std::mutex mEffectSlotLock;
/* Default effect slot */
2019-07-30 09:05:54 -07:00
std::unique_ptr<ALeffectslot> mDefaultSlot;
2020-03-30 13:43:49 -07:00
const char *mExtensionList{nullptr};
ALCcontext(al::intrusive_ptr<ALCdevice> device);
ALCcontext(const ALCcontext&) = delete;
ALCcontext& operator=(const ALCcontext&) = delete;
~ALCcontext();
2018-11-18 00:38:31 -08:00
void init();
/**
* Removes the context from its device and removes it from being current on
* the running thread or globally. Returns true if other contexts still
* exist on the device.
*/
bool deinit();
2019-07-30 14:13:05 -07:00
/**
* Defers/suspends updates for the given context's listener and sources.
* This does *NOT* stop mixing, but rather prevents certain property
2022-02-08 09:39:02 -08:00
* changes from taking effect. mPropLock must be held when called.
2019-07-30 14:13:05 -07:00
*/
2022-02-08 09:39:02 -08:00
bool deferUpdates() noexcept
{ return mDeferUpdates.exchange(true, std::memory_order_acq_rel); }
2019-07-30 14:13:05 -07:00
2022-02-08 09:39:02 -08:00
/**
* Resumes update processing after being deferred. mPropLock must be held
* when called.
*/
2019-07-30 14:13:05 -07:00
void processUpdates();
#ifdef __USE_MINGW_ANSI_STDIO
[[gnu::format(gnu_printf, 3, 4)]]
#else
[[gnu::format(printf, 3, 4)]]
#endif
void setError(ALenum errorCode, const char *msg, ...);
/* Process-wide current context */
static std::atomic<ALCcontext*> sGlobalContext;
private:
/* Thread-local current context. */
static 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();
void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
};
static thread_local ThreadCtx sThreadContext;
public:
/* HACK: MinGW generates bad code when accessing an extern thread_local
* object. Add a wrapper function for it that only accesses it where it's
* defined.
*/
#ifdef __MINGW32__
static ALCcontext *getThreadContext() noexcept;
static void setThreadContext(ALCcontext *context) noexcept;
#else
static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
#endif
/* Default effect that applies to sources that don't have an effect on send 0. */
static ALeffect sDefaultEffect;
2018-11-18 00:38:31 -08:00
DEF_NEWDEL(ALCcontext)
#ifdef ALSOFT_EAX
public:
2022-02-08 09:29:00 -08:00
bool has_eax() const noexcept { return eax_is_initialized_; }
bool eax_is_capable() const noexcept;
void eax_uninitialize() 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 eax_update_filters();
void eax_on_3d_listener_param_call();
void eax_set_last_error() noexcept;
float eax_get_max_filter_gain() const noexcept;
float eax_get_air_absorption_factor() const noexcept;
EaxFxSlotIndex eax_get_previous_primary_fx_slot_index() const noexcept;
EaxFxSlotIndex eax_get_primary_fx_slot_index() const noexcept;
const ALeffectslot& eax_get_fx_slot(
EaxFxSlotIndexValue fx_slot_index) const;
ALeffectslot& eax_get_fx_slot(
EaxFxSlotIndexValue fx_slot_index);
private:
using SourceList = al::vector<SourceSubList>;
struct SourceListIteratorBeginTag{};
struct SourceListIteratorEndTag{};
class SourceListIterator
{
public:
SourceListIterator(
SourceList& sources,
SourceListIteratorBeginTag) noexcept;
SourceListIterator(
SourceList& sources,
SourceListIteratorEndTag) noexcept;
SourceListIterator(
const SourceListIterator& rhs);
SourceListIterator& operator=(
const SourceListIterator& rhs) = delete;
SourceListIterator& operator++();
ALsource& operator*() noexcept;
bool operator==(
const SourceListIterator& rhs) const noexcept;
bool operator!=(
const SourceListIterator& rhs) const noexcept;
private:
SourceList::iterator sub_list_iterator_;
SourceList::iterator sub_list_end_iterator_;
std::uint64_t sub_list_item_index_;
}; // SourceListIterator
class SourceListEnumerator
{
public:
explicit SourceListEnumerator(
SourceList& sources) noexcept;
SourceListEnumerator(
const SourceListEnumerator& rhs) = delete;
SourceListEnumerator& operator=(
const SourceListEnumerator& rhs) = delete;
SourceListIterator begin() noexcept;
SourceListIterator end() noexcept;
private:
SourceList& sources_;
}; // SourceListEnumerator
struct Eax
{
EAX50CONTEXTPROPERTIES context{};
}; // Eax
bool eax_is_initialized_{};
bool eax_is_tried_{};
bool eax_are_legacy_fx_slots_unlocked_{};
long eax_last_error_{};
unsigned long eax_speaker_config_{};
float eax_max_filter_gain_{};
float eax_air_absorption_factor_{};
EaxFxSlotIndex eax_previous_primary_fx_slot_index_{};
EaxFxSlotIndex eax_primary_fx_slot_index_{};
EaxFxSlots eax_fx_slots_{};
EaxContextSharedDirtyFlags eax_context_shared_dirty_flags_{};
Eax eax_{};
Eax eax_d_{};
EAXSESSIONPROPERTIES eax_session_{};
ContextDirtyFlags eax_context_dirty_flags_{};
std::string eax_extension_list_{};
[[noreturn]]
static void eax_fail(
const char* message);
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_initialize_filter_gain();
void eax_set_last_error_defaults() noexcept;
void eax_set_session_defaults() noexcept;
void eax_set_context_defaults() noexcept;
void eax_set_defaults() noexcept;
void eax_initialize_sources();
void eax_unlock_legacy_fx_slots(const EaxEaxCall& eax_call) noexcept;
void eax_dispatch_fx_slot(
const EaxEaxCall& eax_call);
void eax_dispatch_source(
const EaxEaxCall& eax_call);
void eax_get_primary_fx_slot_id(
const EaxEaxCall& eax_call);
void eax_get_distance_factor(
const EaxEaxCall& eax_call);
void eax_get_air_absorption_hf(
const EaxEaxCall& eax_call);
void eax_get_hf_reference(
const EaxEaxCall& eax_call);
void eax_get_last_error(
const EaxEaxCall& eax_call);
void eax_get_speaker_config(
const EaxEaxCall& eax_call);
void eax_get_session(
const EaxEaxCall& eax_call);
void eax_get_macro_fx_factor(
const EaxEaxCall& eax_call);
void eax_get_context_all(
const EaxEaxCall& eax_call);
void eax_get(
const EaxEaxCall& eax_call);
void eax_set_primary_fx_slot_id();
void eax_set_distance_factor();
void eax_set_air_absorbtion_hf();
void eax_set_hf_reference();
void eax_set_macro_fx_factor();
void eax_set_context();
void eax_initialize_fx_slots();
void eax_update_sources();
void eax_validate_primary_fx_slot_id(
const GUID& primary_fx_slot_id);
void eax_validate_distance_factor(
float distance_factor);
void eax_validate_air_absorption_hf(
float air_absorption_hf);
void eax_validate_hf_reference(
float hf_reference);
void eax_validate_speaker_config(
unsigned long speaker_config);
void eax_validate_session_eax_version(
unsigned long eax_version);
void eax_validate_session_max_active_sends(
unsigned long max_active_sends);
void eax_validate_session(
const EAXSESSIONPROPERTIES& eax_session);
void eax_validate_macro_fx_factor(
float macro_fx_factor);
void eax_validate_context_all(
const EAX40CONTEXTPROPERTIES& context_all);
void eax_validate_context_all(
const EAX50CONTEXTPROPERTIES& context_all);
void eax_defer_primary_fx_slot_id(
const GUID& primary_fx_slot_id);
void eax_defer_distance_factor(
float distance_factor);
void eax_defer_air_absorption_hf(
float air_absorption_hf);
void eax_defer_hf_reference(
float hf_reference);
void eax_defer_macro_fx_factor(
float macro_fx_factor);
void eax_defer_context_all(
const EAX40CONTEXTPROPERTIES& context_all);
void eax_defer_context_all(
const EAX50CONTEXTPROPERTIES& context_all);
void eax_defer_context_all(
const EaxEaxCall& eax_call);
void eax_defer_primary_fx_slot_id(
const EaxEaxCall& eax_call);
void eax_defer_distance_factor(
const EaxEaxCall& eax_call);
void eax_defer_air_absorption_hf(
const EaxEaxCall& eax_call);
void eax_defer_hf_reference(
const EaxEaxCall& eax_call);
void eax_set_session(
const EaxEaxCall& eax_call);
void eax_defer_macro_fx_factor(
const EaxEaxCall& eax_call);
void eax_set(
const EaxEaxCall& eax_call);
void eax_apply_deferred();
#endif // ALSOFT_EAX
};
#define SETERR_RETURN(ctx, err, retval, ...) do { \
(ctx)->setError((err), __VA_ARGS__); \
return retval; \
} while(0)
2019-08-01 15:19:37 -07:00
using ContextRef = al::intrusive_ptr<ALCcontext>;
2018-11-24 14:07:32 -08:00
ContextRef GetContextRef(void);
2019-08-01 15:19:37 -07:00
void UpdateContextProps(ALCcontext *context);
extern bool TrapALError;
#ifdef ALSOFT_EAX
ALenum AL_APIENTRY EAXSet(
const GUID* property_set_id,
ALuint property_id,
ALuint property_source_id,
ALvoid* property_value,
ALuint property_value_size) noexcept;
ALenum AL_APIENTRY EAXGet(
const GUID* property_set_id,
ALuint property_id,
ALuint property_source_id,
ALvoid* property_value,
ALuint property_value_size) noexcept;
#endif // ALSOFT_EAX
#endif /* ALC_CONTEXT_H */