|
|
-
- #include "config.h"
-
- #include "context.h"
-
- #include <algorithm>
- #include <functional>
- #include <limits>
- #include <numeric>
- #include <stddef.h>
- #include <stdexcept>
-
- #include "AL/efx.h"
-
- #include "al/auxeffectslot.h"
- #include "al/source.h"
- #include "al/effect.h"
- #include "al/event.h"
- #include "al/listener.h"
- #include "albit.h"
- #include "alc/alu.h"
- #include "core/async_event.h"
- #include "core/device.h"
- #include "core/effectslot.h"
- #include "core/logging.h"
- #include "core/voice.h"
- #include "core/voice_change.h"
- #include "device.h"
- #include "ringbuffer.h"
- #include "vecmat.h"
-
- #ifdef ALSOFT_EAX
- #include <cassert>
- #include <cstring>
-
- #include "alstring.h"
- #include "al/eax/exception.h"
- #include "al/eax/globals.h"
- #endif // ALSOFT_EAX
-
- namespace {
-
- using namespace std::placeholders;
-
- using voidp = void*;
-
- /* Default context extensions */
- constexpr ALchar alExtList[] =
- "AL_EXT_ALAW "
- "AL_EXT_BFORMAT "
- "AL_EXT_DOUBLE "
- "AL_EXT_EXPONENT_DISTANCE "
- "AL_EXT_FLOAT32 "
- "AL_EXT_IMA4 "
- "AL_EXT_LINEAR_DISTANCE "
- "AL_EXT_MCFORMATS "
- "AL_EXT_MULAW "
- "AL_EXT_MULAW_BFORMAT "
- "AL_EXT_MULAW_MCFORMATS "
- "AL_EXT_OFFSET "
- "AL_EXT_source_distance_model "
- "AL_EXT_SOURCE_RADIUS "
- "AL_EXT_STEREO_ANGLES "
- "AL_LOKI_quadriphonic "
- "AL_SOFT_bformat_ex "
- "AL_SOFTX_bformat_hoa "
- "AL_SOFT_block_alignment "
- "AL_SOFT_callback_buffer "
- "AL_SOFTX_convolution_reverb "
- "AL_SOFT_deferred_updates "
- "AL_SOFT_direct_channels "
- "AL_SOFT_direct_channels_remix "
- "AL_SOFT_effect_target "
- "AL_SOFT_events "
- "AL_SOFT_gain_clamp_ex "
- "AL_SOFTX_hold_on_disconnect "
- "AL_SOFT_loop_points "
- "AL_SOFTX_map_buffer "
- "AL_SOFT_MSADPCM "
- "AL_SOFT_source_latency "
- "AL_SOFT_source_length "
- "AL_SOFT_source_resampler "
- "AL_SOFT_source_spatialize "
- "AL_SOFT_UHJ";
-
- } // namespace
-
-
- std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
-
- thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
- ALCcontext::ThreadCtx::~ThreadCtx()
- {
- if(ALCcontext *ctx{ALCcontext::sLocalContext})
- {
- const bool result{ctx->releaseIfNoDelete()};
- ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
- result ? "" : ", leak detected");
- }
- }
- thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
-
- ALeffect ALCcontext::sDefaultEffect;
-
-
- #ifdef __MINGW32__
- ALCcontext *ALCcontext::getThreadContext() noexcept
- { return sLocalContext; }
- void ALCcontext::setThreadContext(ALCcontext *context) noexcept
- { sThreadContext.set(context); }
- #endif
-
- ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
- : ContextBase{device.get()}, mALDevice{std::move(device)}
- {
- }
-
- ALCcontext::~ALCcontext()
- {
- TRACE("Freeing context %p\n", voidp{this});
-
- size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u},
- [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
- { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
- if(count > 0)
- WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
- mSourceList.clear();
- mNumSources = 0;
-
- #ifdef ALSOFT_EAX
- eax_uninitialize();
- #endif // ALSOFT_EAX
-
- mDefaultSlot = nullptr;
- count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
- [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
- { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
- if(count > 0)
- WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
- mEffectSlotList.clear();
- mNumEffectSlots = 0;
- }
-
- void ALCcontext::init()
- {
- if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
- {
- mDefaultSlot = std::make_unique<ALeffectslot>();
- aluInitEffectPanning(&mDefaultSlot->mSlot, this);
- }
-
- EffectSlotArray *auxslots;
- if(!mDefaultSlot)
- auxslots = EffectSlot::CreatePtrArray(0);
- else
- {
- auxslots = EffectSlot::CreatePtrArray(1);
- (*auxslots)[0] = &mDefaultSlot->mSlot;
- mDefaultSlot->mState = SlotState::Playing;
- }
- mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
-
- allocVoiceChanges();
- {
- VoiceChange *cur{mVoiceChangeTail};
- while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
- cur = next;
- mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
- }
-
- mExtensionList = alExtList;
-
- #ifdef ALSOFT_EAX
- eax_initialize_extensions();
- #endif // ALSOFT_EAX
-
- mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
- mParams.Matrix = alu::Matrix::Identity();
- mParams.Velocity = alu::Vector{};
- mParams.Gain = mListener.Gain;
- mParams.MetersPerUnit = mListener.mMetersPerUnit;
- mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
- mParams.DopplerFactor = mDopplerFactor;
- mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
- mParams.SourceDistanceModel = mSourceDistanceModel;
- mParams.mDistanceModel = mDistanceModel;
-
-
- mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
- StartEventThrd(this);
-
-
- allocVoices(256);
- mActiveVoiceCount.store(64, std::memory_order_relaxed);
- }
-
- bool ALCcontext::deinit()
- {
- if(sLocalContext == this)
- {
- WARN("%p released while current on thread\n", voidp{this});
- sThreadContext.set(nullptr);
- release();
- }
-
- ALCcontext *origctx{this};
- if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
- release();
-
- bool ret{};
- /* First make sure this context exists in the device's list. */
- auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
- if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
- {
- using ContextArray = al::FlexArray<ContextBase*>;
- auto alloc_ctx_array = [](const size_t count) -> ContextArray*
- {
- if(count == 0) return &DeviceBase::sEmptyContextArray;
- return ContextArray::Create(count).release();
- };
- auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
-
- /* Copy the current/old context handles to the new array, excluding the
- * given context.
- */
- std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
- std::bind(std::not_equal_to<>{}, _1, this));
-
- /* Store the new context array in the device. Wait for any current mix
- * to finish before deleting the old array.
- */
- mDevice->mContexts.store(newarray);
- if(oldarray != &DeviceBase::sEmptyContextArray)
- {
- mDevice->waitForMix();
- delete oldarray;
- }
-
- ret = !newarray->empty();
- }
- else
- ret = !oldarray->empty();
-
- StopEventThrd(this);
-
- return ret;
- }
-
- void ALCcontext::applyAllUpdates()
- {
- /* Tell the mixer to stop applying updates, then wait for any active
- * updating to finish, before providing updates.
- */
- mHoldUpdates.store(true, std::memory_order_release);
- while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
- /* busy-wait */
- }
-
- #ifdef ALSOFT_EAX
- eax_apply_deferred();
- #endif
- if(std::exchange(mPropsDirty, false))
- UpdateContextProps(this);
- UpdateAllEffectSlotProps(this);
- UpdateAllSourceProps(this);
-
- /* Now with all updates declared, let the mixer continue applying them so
- * they all happen at once.
- */
- mHoldUpdates.store(false, std::memory_order_release);
- }
-
- #ifdef ALSOFT_EAX
- namespace {
-
- class ContextException :
- public EaxException
- {
- public:
- explicit ContextException(
- const char* message)
- :
- EaxException{"EAX_CONTEXT", message}
- {
- }
- }; // ContextException
-
-
- template<typename F>
- void ForEachSource(ALCcontext *context, F func)
- {
- for(auto &sublist : context->mSourceList)
- {
- uint64_t usemask{~sublist.FreeMask};
- while(usemask)
- {
- const int idx{al::countr_zero(usemask)};
- usemask &= ~(1_u64 << idx);
-
- func(sublist.Sources[idx]);
- }
- }
- }
-
- } // namespace
-
-
- bool ALCcontext::eax_is_capable() const noexcept
- {
- return eax_has_enough_aux_sends();
- }
-
- void ALCcontext::eax_uninitialize() noexcept
- {
- if (!eax_is_initialized_)
- {
- return;
- }
-
- eax_is_initialized_ = true;
- eax_is_tried_ = false;
-
- eax_fx_slots_.uninitialize();
- }
-
- ALenum ALCcontext::eax_eax_set(
- const GUID* property_set_id,
- ALuint property_id,
- ALuint property_source_id,
- ALvoid* property_value,
- ALuint property_value_size)
- {
- const auto call = create_eax_call(
- EaxCallType::set,
- property_set_id,
- property_id,
- property_source_id,
- property_value,
- property_value_size);
- eax_version_ = call.get_version();
- eax_initialize(call);
- eax_unlock_legacy_fx_slots(call);
-
- switch (call.get_property_set_id())
- {
- case EaxCallPropertySetId::context:
- eax_set(call);
- break;
-
- case EaxCallPropertySetId::fx_slot:
- case EaxCallPropertySetId::fx_slot_effect:
- eax_dispatch_fx_slot(call);
- break;
-
- case EaxCallPropertySetId::source:
- eax_dispatch_source(call);
- break;
-
- default:
- eax_fail("Unsupported property set id.");
- }
-
- static constexpr auto deferred_flag = 0x80000000u;
- if(!(property_id&deferred_flag) && !mDeferUpdates)
- applyAllUpdates();
-
- return AL_NO_ERROR;
- }
-
- ALenum ALCcontext::eax_eax_get(
- const GUID* property_set_id,
- ALuint property_id,
- ALuint property_source_id,
- ALvoid* property_value,
- ALuint property_value_size)
- {
- const auto call = create_eax_call(
- EaxCallType::get,
- property_set_id,
- property_id,
- property_source_id,
- property_value,
- property_value_size);
- eax_version_ = call.get_version();
- eax_initialize(call);
- eax_unlock_legacy_fx_slots(call);
-
- switch (call.get_property_set_id())
- {
- case EaxCallPropertySetId::context:
- eax_get(call);
- break;
-
- case EaxCallPropertySetId::fx_slot:
- case EaxCallPropertySetId::fx_slot_effect:
- eax_dispatch_fx_slot(call);
- break;
-
- case EaxCallPropertySetId::source:
- eax_dispatch_source(call);
- break;
-
- default:
- eax_fail("Unsupported property set id.");
- }
-
- return AL_NO_ERROR;
- }
-
- void ALCcontext::eax_update_filters()
- {
- ForEachSource(this, [](ALsource& source){ source.eax_commit(); });
- }
-
- void ALCcontext::eax_commit_and_update_sources()
- {
- std::unique_lock<std::mutex> source_lock{mSourceLock};
- ForEachSource(this, std::mem_fn(&ALsource::eax_commit_and_update));
- }
-
- void ALCcontext::eax_set_last_error() noexcept
- {
- eax_last_error_ = EAXERR_INVALID_OPERATION;
- }
-
- [[noreturn]]
- void ALCcontext::eax_fail(
- const char* message)
- {
- throw ContextException{message};
- }
-
- void ALCcontext::eax_initialize_extensions()
- {
- if (!eax_g_is_enabled)
- {
- return;
- }
-
- const auto string_max_capacity =
- std::strlen(mExtensionList) + 1 +
- std::strlen(eax1_ext_name) + 1 +
- std::strlen(eax2_ext_name) + 1 +
- std::strlen(eax3_ext_name) + 1 +
- std::strlen(eax4_ext_name) + 1 +
- std::strlen(eax5_ext_name) + 1 +
- std::strlen(eax_x_ram_ext_name) + 1 +
- 0;
-
- eax_extension_list_.reserve(string_max_capacity);
-
- if (eax_is_capable())
- {
- eax_extension_list_ += eax1_ext_name;
- eax_extension_list_ += ' ';
-
- eax_extension_list_ += eax2_ext_name;
- eax_extension_list_ += ' ';
-
- eax_extension_list_ += eax3_ext_name;
- eax_extension_list_ += ' ';
-
- eax_extension_list_ += eax4_ext_name;
- eax_extension_list_ += ' ';
-
- eax_extension_list_ += eax5_ext_name;
- eax_extension_list_ += ' ';
- }
-
- eax_extension_list_ += eax_x_ram_ext_name;
- eax_extension_list_ += ' ';
-
- eax_extension_list_ += mExtensionList;
- mExtensionList = eax_extension_list_.c_str();
- }
-
- void ALCcontext::eax_initialize(const EaxCall& call)
- {
- if (eax_is_initialized_)
- {
- return;
- }
-
- if (eax_is_tried_)
- {
- eax_fail("No EAX.");
- }
-
- eax_is_tried_ = true;
-
- if (!eax_g_is_enabled)
- {
- eax_fail("EAX disabled by a configuration.");
- }
-
- eax_ensure_compatibility();
- eax_set_defaults();
- eax_set_air_absorbtion_hf();
- eax_update_speaker_configuration();
- eax_initialize_fx_slots(call);
- eax_initialize_sources();
-
- eax_is_initialized_ = true;
- }
-
- bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
- {
- return mDefaultSlot == nullptr;
- }
-
- void ALCcontext::eax_ensure_no_default_effect_slot() const
- {
- if (!eax_has_no_default_effect_slot())
- {
- eax_fail("There is a default effect slot in the context.");
- }
- }
-
- bool ALCcontext::eax_has_enough_aux_sends() const noexcept
- {
- return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS;
- }
-
- void ALCcontext::eax_ensure_enough_aux_sends() const
- {
- if (!eax_has_enough_aux_sends())
- {
- eax_fail("Not enough aux sends.");
- }
- }
-
- void ALCcontext::eax_ensure_compatibility()
- {
- eax_ensure_enough_aux_sends();
- }
-
- unsigned long ALCcontext::eax_detect_speaker_configuration() const
- {
- #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
-
- switch(mDevice->FmtChans)
- {
- case DevFmtMono: return SPEAKERS_2;
- case DevFmtStereo:
- /* Pretend 7.1 if using UHJ output, since they both provide full
- * horizontal surround.
- */
- if(mDevice->mUhjEncoder)
- return SPEAKERS_7;
- if(mDevice->Flags.test(DirectEar))
- return HEADPHONES;
- return SPEAKERS_2;
- case DevFmtQuad: return SPEAKERS_4;
- case DevFmtX51: return SPEAKERS_5;
- case DevFmtX61: return SPEAKERS_6;
- case DevFmtX71: return SPEAKERS_7;
- /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
- * suggest full-sphere surround sound (like HRTF).
- */
- case DevFmtX3D71: return SPEAKERS_5;
- /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
- * provide full-sphere surround sound. Depends if apps are more likely to
- * consider headphones or 7.1 for surround sound support.
- */
- case DevFmtAmbi3D: return SPEAKERS_7;
- }
- ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
- return HEADPHONES;
-
- #undef EAX_PREFIX
- }
-
- void ALCcontext::eax_update_speaker_configuration()
- {
- eax_speaker_config_ = eax_detect_speaker_configuration();
- }
-
- void ALCcontext::eax_set_last_error_defaults() noexcept
- {
- eax_last_error_ = EAX_OK;
- }
-
- void ALCcontext::eax_set_session_defaults() noexcept
- {
- eax_session_.ulEAXVersion = EAXCONTEXT_MINEAXSESSION;
- eax_session_.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
- }
-
- void ALCcontext::eax_set_context_defaults() noexcept
- {
- eax_.context.guidPrimaryFXSlotID = EAXCONTEXT_DEFAULTPRIMARYFXSLOTID;
- eax_.context.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
- eax_.context.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
- eax_.context.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
- }
-
- void ALCcontext::eax_set_defaults() noexcept
- {
- eax_set_last_error_defaults();
- eax_set_session_defaults();
- eax_set_context_defaults();
-
- eax_d_ = eax_;
- }
-
- void ALCcontext::eax_unlock_legacy_fx_slots(const EaxCall& call) noexcept
- {
- if (call.get_version() != 5 || eax_are_legacy_fx_slots_unlocked_)
- return;
-
- eax_are_legacy_fx_slots_unlocked_ = true;
- eax_fx_slots_.unlock_legacy();
- }
-
- void ALCcontext::eax_dispatch_fx_slot(const EaxCall& call)
- {
- const auto fx_slot_index = call.get_fx_slot_index();
- if(!fx_slot_index.has_value())
- eax_fail("Invalid fx slot index.");
-
- auto& fx_slot = eax_get_fx_slot(*fx_slot_index);
- if(fx_slot.eax_dispatch(call))
- {
- std::lock_guard<std::mutex> source_lock{mSourceLock};
- eax_update_filters();
- }
- }
-
- void ALCcontext::eax_dispatch_source(const EaxCall& call)
- {
- const auto source_id = call.get_property_al_name();
- std::lock_guard<std::mutex> source_lock{mSourceLock};
- const auto source = ALsource::eax_lookup_source(*this, source_id);
-
- if (source == nullptr)
- eax_fail("Source not found.");
-
- source->eax_dispatch(call);
- }
-
- void ALCcontext::eax_get_primary_fx_slot_id(const EaxCall& call)
- {
- call.set_value<ContextException>(eax_.context.guidPrimaryFXSlotID);
- }
-
- void ALCcontext::eax_get_distance_factor(const EaxCall& call)
- {
- call.set_value<ContextException>(eax_.context.flDistanceFactor);
- }
-
- void ALCcontext::eax_get_air_absorption_hf(const EaxCall& call)
- {
- call.set_value<ContextException>(eax_.context.flAirAbsorptionHF);
- }
-
- void ALCcontext::eax_get_hf_reference(const EaxCall& call)
- {
- call.set_value<ContextException>(eax_.context.flHFReference);
- }
-
- void ALCcontext::eax_get_last_error(const EaxCall& call)
- {
- const auto eax_last_error = eax_last_error_;
- eax_last_error_ = EAX_OK;
- call.set_value<ContextException>(eax_last_error);
- }
-
- void ALCcontext::eax_get_speaker_config(const EaxCall& call)
- {
- call.set_value<ContextException>(eax_speaker_config_);
- }
-
- void ALCcontext::eax_get_session(const EaxCall& call)
- {
- call.set_value<ContextException>(eax_session_);
- }
-
- void ALCcontext::eax_get_macro_fx_factor(const EaxCall& call)
- {
- call.set_value<ContextException>(eax_.context.flMacroFXFactor);
- }
-
- void ALCcontext::eax_get_context_all(const EaxCall& call)
- {
- switch (call.get_version())
- {
- case 4:
- call.set_value<ContextException>(static_cast<const EAX40CONTEXTPROPERTIES&>(eax_.context));
- break;
-
- case 5:
- call.set_value<ContextException>(static_cast<const EAX50CONTEXTPROPERTIES&>(eax_.context));
- break;
-
- default:
- eax_fail("Unsupported EAX version.");
- }
- }
-
- void ALCcontext::eax_get(const EaxCall& call)
- {
- switch (call.get_property_id())
- {
- case EAXCONTEXT_NONE:
- break;
-
- case EAXCONTEXT_ALLPARAMETERS:
- eax_get_context_all(call);
- break;
-
- case EAXCONTEXT_PRIMARYFXSLOTID:
- eax_get_primary_fx_slot_id(call);
- break;
-
- case EAXCONTEXT_DISTANCEFACTOR:
- eax_get_distance_factor(call);
- break;
-
- case EAXCONTEXT_AIRABSORPTIONHF:
- eax_get_air_absorption_hf(call);
- break;
-
- case EAXCONTEXT_HFREFERENCE:
- eax_get_hf_reference(call);
- break;
-
- case EAXCONTEXT_LASTERROR:
- eax_get_last_error(call);
- break;
-
- case EAXCONTEXT_SPEAKERCONFIG:
- eax_get_speaker_config(call);
- break;
-
- case EAXCONTEXT_EAXSESSION:
- eax_get_session(call);
- break;
-
- case EAXCONTEXT_MACROFXFACTOR:
- eax_get_macro_fx_factor(call);
- break;
-
- default:
- eax_fail("Unsupported property id.");
- }
- }
-
- void ALCcontext::eax_set_primary_fx_slot_id()
- {
- eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
- }
-
- void ALCcontext::eax_set_distance_factor()
- {
- mListener.mMetersPerUnit = eax_.context.flDistanceFactor;
- mPropsDirty = true;
- }
-
- void ALCcontext::eax_set_air_absorbtion_hf()
- {
- mAirAbsorptionGainHF = level_mb_to_gain(eax_.context.flAirAbsorptionHF);
- mPropsDirty = true;
- }
-
- void ALCcontext::eax_set_hf_reference()
- {
- // TODO
- }
-
- void ALCcontext::eax_set_macro_fx_factor()
- {
- // TODO
- }
-
- void ALCcontext::eax_set_context()
- {
- eax_set_primary_fx_slot_id();
- eax_set_distance_factor();
- eax_set_air_absorbtion_hf();
- eax_set_hf_reference();
- }
-
- void ALCcontext::eax_initialize_fx_slots(const EaxCall& call)
- {
- eax_fx_slots_.initialize(call, *this);
- eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
- }
-
- void ALCcontext::eax_initialize_sources()
- {
- std::unique_lock<std::mutex> source_lock{mSourceLock};
- auto init_source = [this](ALsource &source) noexcept
- { source.eax_initialize(this); };
- ForEachSource(this, init_source);
- }
-
- void ALCcontext::eax_update_sources()
- {
- std::unique_lock<std::mutex> source_lock{mSourceLock};
- auto update_source = [](ALsource &source)
- { source.eax_commit(); };
- ForEachSource(this, update_source);
- }
-
- void ALCcontext::eax_validate_primary_fx_slot_id(
- const GUID& primary_fx_slot_id)
- {
- if (primary_fx_slot_id != EAX_NULL_GUID &&
- primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot0 &&
- primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot0 &&
- primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot1 &&
- primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot1 &&
- primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot2 &&
- primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot2 &&
- primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot3 &&
- primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot3)
- {
- eax_fail("Unsupported primary FX slot id.");
- }
- }
-
- void ALCcontext::eax_validate_distance_factor(
- float distance_factor)
- {
- eax_validate_range<ContextException>(
- "Distance Factor",
- distance_factor,
- EAXCONTEXT_MINDISTANCEFACTOR,
- EAXCONTEXT_MAXDISTANCEFACTOR);
- }
-
- void ALCcontext::eax_validate_air_absorption_hf(
- float air_absorption_hf)
- {
- eax_validate_range<ContextException>(
- "Air Absorption HF",
- air_absorption_hf,
- EAXCONTEXT_MINAIRABSORPTIONHF,
- EAXCONTEXT_MAXAIRABSORPTIONHF);
- }
-
- void ALCcontext::eax_validate_hf_reference(
- float hf_reference)
- {
- eax_validate_range<ContextException>(
- "HF Reference",
- hf_reference,
- EAXCONTEXT_MINHFREFERENCE,
- EAXCONTEXT_MAXHFREFERENCE);
- }
-
- void ALCcontext::eax_validate_speaker_config(
- unsigned long speaker_config)
- {
- switch (speaker_config)
- {
- case HEADPHONES:
- case SPEAKERS_2:
- case SPEAKERS_4:
- case SPEAKERS_5:
- case SPEAKERS_6:
- case SPEAKERS_7:
- break;
-
- default:
- eax_fail("Unsupported speaker configuration.");
- }
- }
-
- void ALCcontext::eax_validate_session_eax_version(
- unsigned long eax_version)
- {
- switch (eax_version)
- {
- case EAX_40:
- case EAX_50:
- break;
-
- default:
- eax_fail("Unsupported session EAX version.");
- }
- }
-
- void ALCcontext::eax_validate_session_max_active_sends(
- unsigned long max_active_sends)
- {
- eax_validate_range<ContextException>(
- "Max Active Sends",
- max_active_sends,
- EAXCONTEXT_MINMAXACTIVESENDS,
- EAXCONTEXT_MAXMAXACTIVESENDS);
- }
-
- void ALCcontext::eax_validate_session(
- const EAXSESSIONPROPERTIES& eax_session)
- {
- eax_validate_session_eax_version(eax_session.ulEAXVersion);
- eax_validate_session_max_active_sends(eax_session.ulMaxActiveSends);
- }
-
- void ALCcontext::eax_validate_macro_fx_factor(
- float macro_fx_factor)
- {
- eax_validate_range<ContextException>(
- "Macro FX Factor",
- macro_fx_factor,
- EAXCONTEXT_MINMACROFXFACTOR,
- EAXCONTEXT_MAXMACROFXFACTOR);
- }
-
- void ALCcontext::eax_validate_context_all(
- const EAX40CONTEXTPROPERTIES& context_all)
- {
- eax_validate_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
- eax_validate_distance_factor(context_all.flDistanceFactor);
- eax_validate_air_absorption_hf(context_all.flAirAbsorptionHF);
- eax_validate_hf_reference(context_all.flHFReference);
- }
-
- void ALCcontext::eax_validate_context_all(
- const EAX50CONTEXTPROPERTIES& context_all)
- {
- eax_validate_context_all(static_cast<const EAX40CONTEXTPROPERTIES>(context_all));
- eax_validate_macro_fx_factor(context_all.flMacroFXFactor);
- }
-
- void ALCcontext::eax_defer_primary_fx_slot_id(
- const GUID& primary_fx_slot_id)
- {
- eax_d_.context.guidPrimaryFXSlotID = primary_fx_slot_id;
-
- eax_context_dirty_flags_.guidPrimaryFXSlotID =
- (eax_.context.guidPrimaryFXSlotID != eax_d_.context.guidPrimaryFXSlotID);
- }
-
- void ALCcontext::eax_defer_distance_factor(
- float distance_factor)
- {
- eax_d_.context.flDistanceFactor = distance_factor;
-
- eax_context_dirty_flags_.flDistanceFactor =
- (eax_.context.flDistanceFactor != eax_d_.context.flDistanceFactor);
- }
-
- void ALCcontext::eax_defer_air_absorption_hf(
- float air_absorption_hf)
- {
- eax_d_.context.flAirAbsorptionHF = air_absorption_hf;
-
- eax_context_dirty_flags_.flAirAbsorptionHF =
- (eax_.context.flAirAbsorptionHF != eax_d_.context.flAirAbsorptionHF);
- }
-
- void ALCcontext::eax_defer_hf_reference(
- float hf_reference)
- {
- eax_d_.context.flHFReference = hf_reference;
-
- eax_context_dirty_flags_.flHFReference =
- (eax_.context.flHFReference != eax_d_.context.flHFReference);
- }
-
- void ALCcontext::eax_defer_macro_fx_factor(
- float macro_fx_factor)
- {
- eax_d_.context.flMacroFXFactor = macro_fx_factor;
-
- eax_context_dirty_flags_.flMacroFXFactor =
- (eax_.context.flMacroFXFactor != eax_d_.context.flMacroFXFactor);
- }
-
- void ALCcontext::eax_defer_context_all(
- const EAX40CONTEXTPROPERTIES& context_all)
- {
- eax_defer_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
- eax_defer_distance_factor(context_all.flDistanceFactor);
- eax_defer_air_absorption_hf(context_all.flAirAbsorptionHF);
- eax_defer_hf_reference(context_all.flHFReference);
- }
-
- void ALCcontext::eax_defer_context_all(
- const EAX50CONTEXTPROPERTIES& context_all)
- {
- eax_defer_context_all(static_cast<const EAX40CONTEXTPROPERTIES&>(context_all));
- eax_defer_macro_fx_factor(context_all.flMacroFXFactor);
- }
-
- void ALCcontext::eax_defer_context_all(const EaxCall& call)
- {
- switch(call.get_version())
- {
- case 4:
- {
- const auto& context_all =
- call.get_value<ContextException, EAX40CONTEXTPROPERTIES>();
-
- eax_validate_context_all(context_all);
- eax_defer_context_all(context_all);
- }
- break;
-
- case 5:
- {
- const auto& context_all =
- call.get_value<ContextException, EAX50CONTEXTPROPERTIES>();
-
- eax_validate_context_all(context_all);
- eax_defer_context_all(context_all);
- }
- break;
-
- default:
- eax_fail("Unsupported EAX version.");
- }
- }
-
- void ALCcontext::eax_defer_primary_fx_slot_id(const EaxCall& call)
- {
- const auto& primary_fx_slot_id =
- call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID)>();
-
- eax_validate_primary_fx_slot_id(primary_fx_slot_id);
- eax_defer_primary_fx_slot_id(primary_fx_slot_id);
- }
-
- void ALCcontext::eax_defer_distance_factor(const EaxCall& call)
- {
- const auto& distance_factor =
- call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flDistanceFactor)>();
-
- eax_validate_distance_factor(distance_factor);
- eax_defer_distance_factor(distance_factor);
- }
-
- void ALCcontext::eax_defer_air_absorption_hf(const EaxCall& call)
- {
- const auto& air_absorption_hf =
- call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flAirAbsorptionHF)>();
-
- eax_validate_air_absorption_hf(air_absorption_hf);
- eax_defer_air_absorption_hf(air_absorption_hf);
- }
-
- void ALCcontext::eax_defer_hf_reference(const EaxCall& call)
- {
- const auto& hf_reference =
- call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flHFReference)>();
-
- eax_validate_hf_reference(hf_reference);
- eax_defer_hf_reference(hf_reference);
- }
-
- void ALCcontext::eax_set_session(const EaxCall& call)
- {
- const auto& eax_session =
- call.get_value<ContextException, const EAXSESSIONPROPERTIES>();
-
- eax_validate_session(eax_session);
-
- eax_session_ = eax_session;
- }
-
- void ALCcontext::eax_defer_macro_fx_factor(const EaxCall& call)
- {
- const auto& macro_fx_factor =
- call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flMacroFXFactor)>();
-
- eax_validate_macro_fx_factor(macro_fx_factor);
- eax_defer_macro_fx_factor(macro_fx_factor);
- }
-
- void ALCcontext::eax_set(const EaxCall& call)
- {
- switch (call.get_property_id())
- {
- case EAXCONTEXT_NONE:
- break;
-
- case EAXCONTEXT_ALLPARAMETERS:
- eax_defer_context_all(call);
- break;
-
- case EAXCONTEXT_PRIMARYFXSLOTID:
- eax_defer_primary_fx_slot_id(call);
- break;
-
- case EAXCONTEXT_DISTANCEFACTOR:
- eax_defer_distance_factor(call);
- break;
-
- case EAXCONTEXT_AIRABSORPTIONHF:
- eax_defer_air_absorption_hf(call);
- break;
-
- case EAXCONTEXT_HFREFERENCE:
- eax_defer_hf_reference(call);
- break;
-
- case EAXCONTEXT_LASTERROR:
- eax_fail("Last error is read-only.");
-
- case EAXCONTEXT_SPEAKERCONFIG:
- eax_fail("Speaker configuration is read-only.");
-
- case EAXCONTEXT_EAXSESSION:
- eax_set_session(call);
- break;
-
- case EAXCONTEXT_MACROFXFACTOR:
- eax_defer_macro_fx_factor(call);
- break;
-
- default:
- eax_fail("Unsupported property id.");
- }
- }
-
- void ALCcontext::eax_apply_deferred()
- {
- if (eax_context_dirty_flags_ == ContextDirtyFlags{})
- {
- return;
- }
-
- eax_ = eax_d_;
-
- if (eax_context_dirty_flags_.guidPrimaryFXSlotID)
- {
- eax_set_primary_fx_slot_id();
- }
-
- if (eax_context_dirty_flags_.flDistanceFactor)
- {
- eax_set_distance_factor();
- }
-
- if (eax_context_dirty_flags_.flAirAbsorptionHF)
- {
- eax_set_air_absorbtion_hf();
- }
-
- if (eax_context_dirty_flags_.flHFReference)
- {
- eax_set_hf_reference();
- }
-
- if (eax_context_dirty_flags_.flMacroFXFactor)
- {
- eax_set_macro_fx_factor();
- }
-
- if (eax_context_dirty_flags_.guidPrimaryFXSlotID)
- {
- eax_update_sources();
- }
-
- eax_context_dirty_flags_ = ContextDirtyFlags{};
- }
-
-
- namespace
- {
-
-
- class EaxSetException :
- public EaxException
- {
- public:
- explicit EaxSetException(
- const char* message)
- :
- EaxException{"EAX_SET", message}
- {
- }
- }; // EaxSetException
-
-
- [[noreturn]]
- void eax_fail_set(
- const char* message)
- {
- throw EaxSetException{message};
- }
-
-
- class EaxGetException :
- public EaxException
- {
- public:
- explicit EaxGetException(
- const char* message)
- :
- EaxException{"EAX_GET", message}
- {
- }
- }; // EaxGetException
-
-
- [[noreturn]]
- void eax_fail_get(
- const char* message)
- {
- throw EaxGetException{message};
- }
-
-
- } // namespace
-
-
- FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
- const GUID* property_set_id,
- ALuint property_id,
- ALuint property_source_id,
- ALvoid* property_value,
- ALuint property_value_size) noexcept
- try
- {
- auto context = GetContextRef();
-
- if (!context)
- {
- eax_fail_set("No current context.");
- }
-
- std::lock_guard<std::mutex> prop_lock{context->mPropLock};
-
- return context->eax_eax_set(
- property_set_id,
- property_id,
- property_source_id,
- property_value,
- property_value_size
- );
- }
- catch (...)
- {
- eax_log_exception(__func__);
- return AL_INVALID_OPERATION;
- }
-
- FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
- const GUID* property_set_id,
- ALuint property_id,
- ALuint property_source_id,
- ALvoid* property_value,
- ALuint property_value_size) noexcept
- try
- {
- auto context = GetContextRef();
-
- if (!context)
- {
- eax_fail_get("No current context.");
- }
-
- std::lock_guard<std::mutex> prop_lock{context->mPropLock};
-
- return context->eax_eax_get(
- property_set_id,
- property_id,
- property_source_id,
- property_value,
- property_value_size
- );
- }
- catch (...)
- {
- eax_log_exception(__func__);
- return AL_INVALID_OPERATION;
- }
- #endif // ALSOFT_EAX
|