2021-05-14 10:15:42 +08:00
|
|
|
|
|
|
|
#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"
|
2022-04-25 12:02:45 +08:00
|
|
|
#include "core/effectslot.h"
|
2021-05-14 10:15:42 +08:00
|
|
|
#include "core/logging.h"
|
|
|
|
#include "core/voice.h"
|
|
|
|
#include "core/voice_change.h"
|
|
|
|
#include "device.h"
|
|
|
|
#include "ringbuffer.h"
|
|
|
|
#include "vecmat.h"
|
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
#ifdef ALSOFT_EAX
|
|
|
|
#include <cstring>
|
|
|
|
#include "alstring.h"
|
2022-07-14 23:17:11 +08:00
|
|
|
#include "al/eax/globals.h"
|
2022-04-25 12:02:45 +08:00
|
|
|
#endif // ALSOFT_EAX
|
2021-05-14 10:15:42 +08:00
|
|
|
|
|
|
|
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 "
|
2022-04-25 12:02:45 +08:00
|
|
|
"AL_SOFT_callback_buffer "
|
2021-05-14 10:15:42 +08:00
|
|
|
"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 "
|
2023-02-04 15:03:54 +08:00
|
|
|
"AL_SOFTX_source_start_delay "
|
2022-04-25 12:02:45 +08:00
|
|
|
"AL_SOFT_UHJ";
|
2021-05-14 10:15:42 +08:00
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
std::atomic<bool> ALCcontext::sGlobalContextLock{false};
|
2021-05-14 10:15:42 +08:00
|
|
|
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;
|
|
|
|
|
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
#ifdef __MINGW32__
|
|
|
|
ALCcontext *ALCcontext::getThreadContext() noexcept
|
|
|
|
{ return sLocalContext; }
|
|
|
|
void ALCcontext::setThreadContext(ALCcontext *context) noexcept
|
|
|
|
{ sThreadContext.set(context); }
|
|
|
|
#endif
|
2021-05-14 10:15:42 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
#ifdef ALSOFT_EAX
|
|
|
|
eax_uninitialize();
|
|
|
|
#endif // ALSOFT_EAX
|
|
|
|
|
2021-05-14 10:15:42 +08:00
|
|
|
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)
|
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
mDefaultSlot = std::make_unique<ALeffectslot>(this);
|
|
|
|
aluInitEffectPanning(mDefaultSlot->mSlot, this);
|
2021-05-14 10:15:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
EffectSlotArray *auxslots;
|
|
|
|
if(!mDefaultSlot)
|
|
|
|
auxslots = EffectSlot::CreatePtrArray(0);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auxslots = EffectSlot::CreatePtrArray(1);
|
2023-02-04 15:03:54 +08:00
|
|
|
(*auxslots)[0] = mDefaultSlot->mSlot;
|
2021-05-14 10:15:42 +08:00
|
|
|
mDefaultSlot->mState = SlotState::Playing;
|
|
|
|
}
|
|
|
|
mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
|
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
allocVoiceChanges();
|
2021-05-14 10:15:42 +08:00
|
|
|
{
|
|
|
|
VoiceChange *cur{mVoiceChangeTail};
|
|
|
|
while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
|
|
|
|
cur = next;
|
|
|
|
mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
|
|
|
|
}
|
|
|
|
|
|
|
|
mExtensionList = alExtList;
|
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
#ifdef ALSOFT_EAX
|
|
|
|
eax_initialize_extensions();
|
|
|
|
#endif // ALSOFT_EAX
|
2021-05-14 10:15:42 +08:00
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
|
2021-05-14 10:15:42 +08:00
|
|
|
mParams.Matrix = alu::Matrix::Identity();
|
|
|
|
mParams.Velocity = alu::Vector{};
|
|
|
|
mParams.Gain = mListener.Gain;
|
|
|
|
mParams.MetersPerUnit = mListener.mMetersPerUnit;
|
2022-04-25 12:02:45 +08:00
|
|
|
mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
|
2021-05-14 10:15:42 +08:00
|
|
|
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);
|
2023-02-04 15:03:54 +08:00
|
|
|
dec_ref();
|
2021-05-14 10:15:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ALCcontext *origctx{this};
|
|
|
|
if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
|
2023-02-04 15:03:54 +08:00
|
|
|
{
|
|
|
|
while(sGlobalContextLock.load()) {
|
|
|
|
/* Wait to make sure another thread didn't get the context and is
|
|
|
|
* trying to increment its refcount.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
dec_ref();
|
|
|
|
}
|
2021-05-14 10:15:42 +08:00
|
|
|
|
|
|
|
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(),
|
2023-02-04 15:03:54 +08:00
|
|
|
[this](auto a){ return a != this; });
|
2021-05-14 10:15:42 +08:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
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
|
2023-02-04 15:03:54 +08:00
|
|
|
if(eax_is_initialized_)
|
|
|
|
eax_commit();
|
2022-04-25 12:02:45 +08:00
|
|
|
#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 {
|
|
|
|
|
|
|
|
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]);
|
2021-05-14 10:15:42 +08:00
|
|
|
}
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
bool ALCcontext::eax_is_capable() const noexcept
|
|
|
|
{
|
|
|
|
return eax_has_enough_aux_sends();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALCcontext::eax_uninitialize() noexcept
|
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
if(!eax_is_initialized_)
|
2022-04-25 12:02:45 +08:00
|
|
|
return;
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_is_initialized_ = false;
|
2022-04-25 12:02:45 +08:00
|
|
|
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)
|
|
|
|
{
|
2022-07-14 23:17:11 +08:00
|
|
|
const auto call = create_eax_call(
|
|
|
|
EaxCallType::set,
|
2022-04-25 12:02:45 +08:00
|
|
|
property_set_id,
|
|
|
|
property_id,
|
|
|
|
property_source_id,
|
|
|
|
property_value,
|
2022-07-14 23:17:11 +08:00
|
|
|
property_value_size);
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
const auto eax_version = call.get_version();
|
|
|
|
if(eax_version != eax_version_)
|
|
|
|
eax_df_ = ~EaxDirtyFlags();
|
|
|
|
eax_version_ = eax_version;
|
|
|
|
eax_initialize();
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
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_unknown_property_set_id();
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(!call.is_deferred() && !mDeferUpdates)
|
2022-04-25 12:02:45 +08:00
|
|
|
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)
|
|
|
|
{
|
2022-07-14 23:17:11 +08:00
|
|
|
const auto call = create_eax_call(
|
|
|
|
EaxCallType::get,
|
2022-04-25 12:02:45 +08:00
|
|
|
property_set_id,
|
|
|
|
property_id,
|
|
|
|
property_source_id,
|
|
|
|
property_value,
|
2022-07-14 23:17:11 +08:00
|
|
|
property_value_size);
|
|
|
|
eax_version_ = call.get_version();
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_initialize();
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(call.get_property_set_id())
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
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_unknown_property_set_id();
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return AL_NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
[[noreturn]] void ALCcontext::eax_fail(const char* message)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
|
|
|
throw ContextException{message};
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
[[noreturn]] void ALCcontext::eax_fail_unknown_property_set_id()
|
|
|
|
{
|
|
|
|
eax_fail("Unknown property ID.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[[noreturn]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
|
|
|
|
{
|
|
|
|
eax_fail("Unknown primary FX Slot ID.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[[noreturn]] void ALCcontext::eax_fail_unknown_property_id()
|
|
|
|
{
|
|
|
|
eax_fail("Unknown property ID.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[[noreturn]] void ALCcontext::eax_fail_unknown_version()
|
|
|
|
{
|
|
|
|
eax_fail("Unknown version.");
|
|
|
|
}
|
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
void ALCcontext::eax_initialize_extensions()
|
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
if(!eax_g_is_enabled)
|
2022-04-25 12:02:45 +08:00
|
|
|
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 +
|
2023-02-04 15:03:54 +08:00
|
|
|
std::strlen(eax_x_ram_ext_name) + 1;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
|
|
|
eax_extension_list_.reserve(string_max_capacity);
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(eax_is_capable()) {
|
2022-04-25 12:02:45 +08:00
|
|
|
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_ += ' ';
|
2021-05-14 10:15:42 +08:00
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
eax_extension_list_ += mExtensionList;
|
|
|
|
mExtensionList = eax_extension_list_.c_str();
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_initialize()
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
if(eax_is_initialized_)
|
2022-04-25 12:02:45 +08:00
|
|
|
return;
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(eax_is_tried_)
|
2022-04-25 12:02:45 +08:00
|
|
|
eax_fail("No EAX.");
|
|
|
|
|
|
|
|
eax_is_tried_ = true;
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(!eax_g_is_enabled)
|
2022-04-25 12:02:45 +08:00
|
|
|
eax_fail("EAX disabled by a configuration.");
|
|
|
|
|
|
|
|
eax_ensure_compatibility();
|
|
|
|
eax_set_defaults();
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_context_commit_air_absorbtion_hf();
|
2022-04-25 12:02:45 +08:00
|
|
|
eax_update_speaker_configuration();
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_initialize_fx_slots();
|
2022-04-25 12:02:45 +08:00
|
|
|
eax_initialize_sources();
|
|
|
|
|
|
|
|
eax_is_initialized_ = true;
|
2023-02-04 15:03:54 +08:00
|
|
|
mPropsDirty = true;
|
|
|
|
|
|
|
|
if(!mDeferUpdates)
|
|
|
|
applyAllUpdates();
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
2021-05-14 10:15:42 +08:00
|
|
|
|
2022-04-25 12:02:45 +08:00
|
|
|
bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
|
|
|
|
{
|
|
|
|
return mDefaultSlot == nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALCcontext::eax_ensure_no_default_effect_slot() const
|
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
if(!eax_has_no_default_effect_slot())
|
2022-04-25 12:02:45 +08:00
|
|
|
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
|
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
if(!eax_has_enough_aux_sends())
|
2022-04-25 12:02:45 +08:00
|
|
|
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.
|
2021-05-14 10:15:42 +08:00
|
|
|
*/
|
2022-04-25 12:02:45 +08:00
|
|
|
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;
|
2023-02-04 15:03:54 +08:00
|
|
|
/* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
|
|
|
|
* suggest with-height surround sound (like HRTF).
|
|
|
|
*/
|
|
|
|
case DevFmtX714: return SPEAKERS_7;
|
2022-07-14 23:17:11 +08:00
|
|
|
/* 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;
|
2022-04-25 12:02:45 +08:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_session_set_defaults() noexcept
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_session_.ulEAXVersion = EAXCONTEXT_DEFAULTEAXSESSION;
|
2022-04-25 12:02:45 +08:00
|
|
|
eax_session_.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax4_context_set_defaults(Eax4Props& props) noexcept
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
props.guidPrimaryFXSlotID = EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
|
|
|
|
props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
|
|
|
|
props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
|
|
|
|
props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax4_context_set_defaults(Eax4State& state) noexcept
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
eax4_context_set_defaults(state.i);
|
|
|
|
state.d = state.i;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALCcontext::eax5_context_set_defaults(Eax5Props& props) noexcept
|
|
|
|
{
|
|
|
|
props.guidPrimaryFXSlotID = EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID;
|
|
|
|
props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
|
|
|
|
props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
|
|
|
|
props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
|
|
|
|
props.flMacroFXFactor = EAXCONTEXT_DEFAULTMACROFXFACTOR;
|
|
|
|
}
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax5_context_set_defaults(Eax5State& state) noexcept
|
|
|
|
{
|
|
|
|
eax5_context_set_defaults(state.i);
|
|
|
|
state.d = state.i;
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax4_context_set_current_defaults(const Eax4Props& props) noexcept
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
static_cast<Eax4Props&>(eax_) = props;
|
|
|
|
eax_.flMacroFXFactor = EAXCONTEXT_DEFAULTMACROFXFACTOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALCcontext::eax5_context_set_current_defaults(const Eax5Props& props) noexcept
|
|
|
|
{
|
|
|
|
eax_ = props;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALCcontext::eax_context_set_current_defaults()
|
|
|
|
{
|
|
|
|
switch(eax_version_)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
eax5_context_set_current_defaults(eax123_.i);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
eax4_context_set_current_defaults(eax4_.i);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
eax5_context_set_current_defaults(eax5_.i);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
eax_fail_unknown_version();
|
|
|
|
}
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_df_ = ~EaxDirtyFlags{};
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALCcontext::eax_context_set_defaults()
|
|
|
|
{
|
|
|
|
eax5_context_set_defaults(eax123_);
|
|
|
|
eax4_context_set_defaults(eax4_);
|
|
|
|
eax5_context_set_defaults(eax5_);
|
|
|
|
eax_context_set_current_defaults();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALCcontext::eax_set_defaults()
|
|
|
|
{
|
|
|
|
eax_set_last_error_defaults();
|
|
|
|
eax_session_set_defaults();
|
|
|
|
eax_context_set_defaults();
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2022-07-14 23:17:11 +08:00
|
|
|
void ALCcontext::eax_dispatch_fx_slot(const EaxCall& call)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2022-07-14 23:17:11 +08:00
|
|
|
const auto fx_slot_index = call.get_fx_slot_index();
|
2022-04-25 12:02:45 +08:00
|
|
|
if(!fx_slot_index.has_value())
|
|
|
|
eax_fail("Invalid fx slot index.");
|
|
|
|
|
|
|
|
auto& fx_slot = eax_get_fx_slot(*fx_slot_index);
|
2022-07-14 23:17:11 +08:00
|
|
|
if(fx_slot.eax_dispatch(call))
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> source_lock{mSourceLock};
|
2023-02-04 15:03:54 +08:00
|
|
|
ForEachSource(this, [](ALsource& source){ source.eax_mark_as_changed(); });
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-14 23:17:11 +08:00
|
|
|
void ALCcontext::eax_dispatch_source(const EaxCall& call)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2022-07-14 23:17:11 +08:00
|
|
|
const auto source_id = call.get_property_al_name();
|
2022-04-25 12:02:45 +08:00
|
|
|
std::lock_guard<std::mutex> source_lock{mSourceLock};
|
|
|
|
const auto source = ALsource::eax_lookup_source(*this, source_id);
|
|
|
|
|
2022-07-14 23:17:11 +08:00
|
|
|
if (source == nullptr)
|
2022-04-25 12:02:45 +08:00
|
|
|
eax_fail("Source not found.");
|
|
|
|
|
2022-07-14 23:17:11 +08:00
|
|
|
source->eax_dispatch(call);
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_get_misc(const EaxCall& call)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(call.get_property_id())
|
|
|
|
{
|
|
|
|
case EAXCONTEXT_NONE:
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_LASTERROR:
|
|
|
|
call.set_value<ContextException>(eax_last_error_);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_SPEAKERCONFIG:
|
|
|
|
call.set_value<ContextException>(eax_speaker_config_);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_EAXSESSION:
|
|
|
|
call.set_value<ContextException>(eax_session_);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
eax_fail_unknown_property_id();
|
|
|
|
}
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax4_get(const EaxCall& call, const Eax4Props& props)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(call.get_property_id())
|
|
|
|
{
|
|
|
|
case EAXCONTEXT_ALLPARAMETERS:
|
|
|
|
call.set_value<ContextException>(props);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_PRIMARYFXSLOTID:
|
|
|
|
call.set_value<ContextException>(props.guidPrimaryFXSlotID);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_DISTANCEFACTOR:
|
|
|
|
call.set_value<ContextException>(props.flDistanceFactor);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_AIRABSORPTIONHF:
|
|
|
|
call.set_value<ContextException>(props.flAirAbsorptionHF);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_HFREFERENCE:
|
|
|
|
call.set_value<ContextException>(props.flHFReference);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
eax_get_misc(call);
|
|
|
|
break;
|
|
|
|
}
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax5_get(const EaxCall& call, const Eax5Props& props)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(call.get_property_id())
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
case EAXCONTEXT_ALLPARAMETERS:
|
|
|
|
call.set_value<ContextException>(props);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_PRIMARYFXSLOTID:
|
|
|
|
call.set_value<ContextException>(props.guidPrimaryFXSlotID);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_DISTANCEFACTOR:
|
|
|
|
call.set_value<ContextException>(props.flDistanceFactor);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_AIRABSORPTIONHF:
|
|
|
|
call.set_value<ContextException>(props.flAirAbsorptionHF);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_HFREFERENCE:
|
|
|
|
call.set_value<ContextException>(props.flHFReference);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_MACROFXFACTOR:
|
|
|
|
call.set_value<ContextException>(props.flMacroFXFactor);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
eax_get_misc(call);
|
|
|
|
break;
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-14 23:17:11 +08:00
|
|
|
void ALCcontext::eax_get(const EaxCall& call)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(call.get_version())
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
case 4: eax4_get(call, eax4_.i); break;
|
|
|
|
case 5: eax5_get(call, eax5_.i); break;
|
|
|
|
default: eax_fail_unknown_version();
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_context_commit_primary_fx_slot_id()
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_primary_fx_slot_index_ = eax_.guidPrimaryFXSlotID;
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_context_commit_distance_factor()
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
if(mListener.mMetersPerUnit == eax_.flDistanceFactor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mListener.mMetersPerUnit = eax_.flDistanceFactor;
|
2022-04-25 12:02:45 +08:00
|
|
|
mPropsDirty = true;
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_context_commit_air_absorbtion_hf()
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
const auto new_value = level_mb_to_gain(eax_.flAirAbsorptionHF);
|
|
|
|
|
|
|
|
if(mAirAbsorptionGainHF == new_value)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mAirAbsorptionGainHF = new_value;
|
2022-04-25 12:02:45 +08:00
|
|
|
mPropsDirty = true;
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_context_commit_hf_reference()
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_context_commit_macro_fx_factor()
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_initialize_fx_slots()
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_fx_slots_.initialize(*this);
|
|
|
|
eax_primary_fx_slot_index_ = eax_.guidPrimaryFXSlotID;
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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};
|
2022-07-14 23:17:11 +08:00
|
|
|
auto update_source = [](ALsource &source)
|
|
|
|
{ source.eax_commit(); };
|
2022-04-25 12:02:45 +08:00
|
|
|
ForEachSource(this, update_source);
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_set_misc(const EaxCall& call)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(call.get_property_id())
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
case EAXCONTEXT_NONE:
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_SPEAKERCONFIG:
|
|
|
|
eax_set<Eax5SpeakerConfigValidator>(call, eax_speaker_config_);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_EAXSESSION:
|
|
|
|
eax_set<Eax5SessionAllValidator>(call, eax_session_);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
eax_fail_unknown_property_id();
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax4_defer_all(const EaxCall& call, Eax4State& state)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
const auto& src = call.get_value<ContextException, const EAX40CONTEXTPROPERTIES>();
|
|
|
|
Eax4AllValidator{}(src);
|
|
|
|
const auto& dst_i = state.i;
|
|
|
|
auto& dst_d = state.d;
|
|
|
|
dst_d = src;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
|
|
|
|
eax_df_ |= eax_primary_fx_slot_id_dirty_bit;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
|
|
|
|
eax_df_ |= eax_distance_factor_dirty_bit;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
|
|
|
|
eax_df_ |= eax_air_absorption_hf_dirty_bit;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_i.flHFReference != dst_d.flHFReference)
|
|
|
|
eax_df_ |= eax_hf_reference_dirty_bit;
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax4_defer(const EaxCall& call, Eax4State& state)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(call.get_property_id())
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
case EAXCONTEXT_ALLPARAMETERS:
|
|
|
|
eax4_defer_all(call, state);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_PRIMARYFXSLOTID:
|
|
|
|
eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
|
|
|
|
call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_DISTANCEFACTOR:
|
|
|
|
eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
|
|
|
|
call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_AIRABSORPTIONHF:
|
|
|
|
eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
|
|
|
|
call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_HFREFERENCE:
|
|
|
|
eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
|
|
|
|
call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
eax_set_misc(call);
|
|
|
|
break;
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax5_defer_all(const EaxCall& call, Eax5State& state)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
const auto& src = call.get_value<ContextException, const EAX50CONTEXTPROPERTIES>();
|
|
|
|
Eax4AllValidator{}(src);
|
|
|
|
const auto& dst_i = state.i;
|
|
|
|
auto& dst_d = state.d;
|
|
|
|
dst_d = src;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
|
|
|
|
eax_df_ |= eax_primary_fx_slot_id_dirty_bit;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
|
|
|
|
eax_df_ |= eax_distance_factor_dirty_bit;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
|
|
|
|
eax_df_ |= eax_air_absorption_hf_dirty_bit;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_i.flHFReference != dst_d.flHFReference)
|
|
|
|
eax_df_ |= eax_hf_reference_dirty_bit;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_i.flMacroFXFactor != dst_d.flMacroFXFactor)
|
|
|
|
eax_df_ |= eax_macro_fx_factor_dirty_bit;
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax5_defer(const EaxCall& call, Eax5State& state)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(call.get_property_id())
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
case EAXCONTEXT_ALLPARAMETERS:
|
|
|
|
eax5_defer_all(call, state);
|
2022-04-25 12:02:45 +08:00
|
|
|
break;
|
2023-02-04 15:03:54 +08:00
|
|
|
case EAXCONTEXT_PRIMARYFXSLOTID:
|
|
|
|
eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
|
|
|
|
call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_DISTANCEFACTOR:
|
|
|
|
eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
|
|
|
|
call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_AIRABSORPTIONHF:
|
|
|
|
eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
|
|
|
|
call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_HFREFERENCE:
|
|
|
|
eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
|
|
|
|
call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
|
|
|
|
break;
|
|
|
|
case EAXCONTEXT_MACROFXFACTOR:
|
|
|
|
eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
|
|
|
|
call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
|
2022-04-25 12:02:45 +08:00
|
|
|
break;
|
|
|
|
default:
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_set_misc(call);
|
|
|
|
break;
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_set(const EaxCall& call)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(call.get_version())
|
|
|
|
{
|
|
|
|
case 4: eax4_defer(call, eax4_); break;
|
|
|
|
case 5: eax5_defer(call, eax5_); break;
|
|
|
|
default: eax_fail_unknown_version();
|
|
|
|
}
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
if(eax_df_ == EaxDirtyFlags{})
|
|
|
|
return;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
|
|
|
|
state, dst_df, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
|
|
|
eax_context_commit_property<eax_distance_factor_dirty_bit>(
|
|
|
|
state, dst_df, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
|
|
|
|
eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
|
|
|
|
state, dst_df, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
|
|
|
|
eax_context_commit_property<eax_hf_reference_dirty_bit>(
|
|
|
|
state, dst_df, &EAX40CONTEXTPROPERTIES::flHFReference);
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_df_ = EaxDirtyFlags{};
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
if(eax_df_ == EaxDirtyFlags{})
|
|
|
|
return;
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
|
|
|
|
state, dst_df, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
|
|
|
eax_context_commit_property<eax_distance_factor_dirty_bit>(
|
|
|
|
state, dst_df, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
|
|
|
|
eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
|
|
|
|
state, dst_df, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
|
|
|
|
eax_context_commit_property<eax_hf_reference_dirty_bit>(
|
|
|
|
state, dst_df, &EAX50CONTEXTPROPERTIES::flHFReference);
|
|
|
|
eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(
|
|
|
|
state, dst_df, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_df_ = EaxDirtyFlags{};
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_context_commit()
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
auto dst_df = EaxDirtyFlags{};
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
switch(eax_version_)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
eax5_context_commit(eax123_, dst_df);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
eax4_context_commit(eax4_, dst_df);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
eax5_context_commit(eax5_, dst_df);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
eax_fail_unknown_version();
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(dst_df == EaxDirtyFlags{})
|
2022-04-25 12:02:45 +08:00
|
|
|
return;
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
|
|
|
|
eax_context_commit_primary_fx_slot_id();
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if((dst_df & eax_distance_factor_dirty_bit) != EaxDirtyFlags{})
|
|
|
|
eax_context_commit_distance_factor();
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if((dst_df & eax_air_absorption_hf_dirty_bit) != EaxDirtyFlags{})
|
|
|
|
eax_context_commit_air_absorbtion_hf();
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if((dst_df & eax_hf_reference_dirty_bit) != EaxDirtyFlags{})
|
|
|
|
eax_context_commit_hf_reference();
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if((dst_df & eax_macro_fx_factor_dirty_bit) != EaxDirtyFlags{})
|
|
|
|
eax_context_commit_macro_fx_factor();
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
|
2022-04-25 12:02:45 +08:00
|
|
|
eax_update_sources();
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
void ALCcontext::eax_commit()
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
2023-02-04 15:03:54 +08:00
|
|
|
eax_context_commit();
|
|
|
|
}
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
namespace {
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
class EaxSetException : public EaxException {
|
2022-04-25 12:02:45 +08:00
|
|
|
public:
|
2023-02-04 15:03:54 +08:00
|
|
|
explicit EaxSetException(const char* message)
|
|
|
|
: EaxException{"EAX_SET", message}
|
|
|
|
{}
|
|
|
|
};
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
[[noreturn]] void eax_fail_set(const char* message)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
|
|
|
throw EaxSetException{message};
|
|
|
|
}
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
class EaxGetException : public EaxException {
|
2022-04-25 12:02:45 +08:00
|
|
|
public:
|
2023-02-04 15:03:54 +08:00
|
|
|
explicit EaxGetException(const char* message)
|
|
|
|
: EaxException{"EAX_GET", message}
|
|
|
|
{}
|
|
|
|
};
|
2022-04-25 12:02:45 +08:00
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
[[noreturn]] void eax_fail_get(const char* message)
|
2022-04-25 12:02:45 +08:00
|
|
|
{
|
|
|
|
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();
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(!context)
|
2022-04-25 12:02:45 +08:00
|
|
|
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,
|
2023-02-04 15:03:54 +08:00
|
|
|
property_value_size);
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
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();
|
|
|
|
|
2023-02-04 15:03:54 +08:00
|
|
|
if(!context)
|
2022-04-25 12:02:45 +08:00
|
|
|
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,
|
2023-02-04 15:03:54 +08:00
|
|
|
property_value_size);
|
2022-04-25 12:02:45 +08:00
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
eax_log_exception(__func__);
|
|
|
|
return AL_INVALID_OPERATION;
|
2021-05-14 10:15:42 +08:00
|
|
|
}
|
2022-04-25 12:02:45 +08:00
|
|
|
#endif // ALSOFT_EAX
|