aboutsummaryrefslogtreecommitdiffstats
path: root/al/auxeffectslot.cpp
diff options
context:
space:
mode:
authorBoris I. Bendovsky <[email protected]>2022-01-30 14:47:32 +0200
committerGitHub <[email protected]>2022-01-30 04:47:32 -0800
commit19ed994dc30ed84ea7cbbb5152577669fc25caf6 (patch)
treef68933bf8f778806618bd6c0b1bf9ced1b0ccf08 /al/auxeffectslot.cpp
parent619249371a40f03cf988d1f5750d643df797c485 (diff)
Add EAX extensions (EAX 2.0-5.0, X-RAM) (#632)
* Add EAX extensions (EAX 2.0-5.0, X-RAM) * Comment out C++17 leftovers * Remove everything related to patching * Update alsoftrc.sample * Rewrite integration * Fix GCC compilation under Linux * Always reset EAX effect properties when loading it into FX slot
Diffstat (limited to 'al/auxeffectslot.cpp')
-rw-r--r--al/auxeffectslot.cpp834
1 files changed, 834 insertions, 0 deletions
diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp
index 16e4cbea..40949aa3 100644
--- a/al/auxeffectslot.cpp
+++ b/al/auxeffectslot.cpp
@@ -50,6 +50,10 @@
#include "effect.h"
#include "opthelpers.h"
+#if ALSOFT_EAX
+#include "eax_exception.h"
+#include "eax_utils.h"
+#endif // ALSOFT_EAX
namespace {
@@ -1042,3 +1046,833 @@ EffectSlotSubList::~EffectSlotSubList()
al_free(EffectSlots);
EffectSlots = nullptr;
}
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+class EaxFxSlotException :
+ public EaxException
+{
+public:
+ explicit EaxFxSlotException(
+ const char* message)
+ :
+ EaxException{"EAX_FX_SLOT", message}
+ {
+ }
+}; // EaxFxSlotException
+
+
+} // namespace
+
+
+void ALeffectslot::eax_initialize(
+ ALCcontext& al_context,
+ EaxFxSlotIndexValue index)
+{
+ eax_al_context_ = &al_context;
+
+ if (index >= EAX_MAX_FXSLOTS)
+ {
+ eax_fail("Index out of range.");
+ }
+
+ eax_fx_slot_index_ = index;
+
+ eax_initialize_eax();
+ eax_initialize_effects();
+ eax_set_default_slots_defaults();
+}
+
+void ALeffectslot::eax_uninitialize() noexcept
+{
+ eax_al_effect_ = nullptr;
+}
+
+const EAX50FXSLOTPROPERTIES& ALeffectslot::eax_get_eax_fx_slot() const noexcept
+{
+ return eax_eax_fx_slot_;
+}
+
+void ALeffectslot::eax_validate_fx_slot_effect(
+ const GUID& eax_effect_id)
+{
+ if (eax_effect_id != EAX_NULL_GUID &&
+ eax_effect_id != EAX_REVERB_EFFECT)
+ {
+ eax_fail("Unsupported EAX effect GUID.");
+ }
+}
+
+void ALeffectslot::eax_validate_fx_slot_volume(
+ long eax_volume)
+{
+ eax_validate_range<EaxFxSlotException>(
+ "Volume",
+ eax_volume,
+ EAXFXSLOT_MINVOLUME,
+ EAXFXSLOT_MAXVOLUME);
+}
+
+void ALeffectslot::eax_validate_fx_slot_lock(
+ long eax_lock)
+{
+ eax_validate_range<EaxFxSlotException>(
+ "Lock",
+ eax_lock,
+ EAXFXSLOT_MINLOCK,
+ EAXFXSLOT_MAXLOCK);
+}
+
+void ALeffectslot::eax_validate_fx_slot_lock_state(
+ long eax_lock,
+ const GUID& eax_effect_id)
+{
+ if (eax_lock == EAXFXSLOT_LOCKED && eax_effect_id != eax_eax_fx_slot_.guidLoadEffect)
+ {
+ eax_fail("Loading an effect in a locked slot.");
+ }
+}
+
+void ALeffectslot::eax_validate_fx_slot_flags(
+ unsigned long eax_flags,
+ int eax_version)
+{
+ eax_validate_range<EaxFxSlotException>(
+ "Flags",
+ eax_flags,
+ 0UL,
+ ~(eax_version == 4 ? EAX40FXSLOTFLAGS_RESERVED : EAX50FXSLOTFLAGS_RESERVED));
+}
+
+void ALeffectslot::eax_validate_fx_slot_occlusion(
+ long eax_occlusion)
+{
+ eax_validate_range<EaxFxSlotException>(
+ "Occlusion",
+ eax_occlusion,
+ EAXFXSLOT_MINOCCLUSION,
+ EAXFXSLOT_MAXOCCLUSION);
+}
+
+void ALeffectslot::eax_validate_fx_slot_occlusion_lf_ratio(
+ float eax_occlusion_lf_ratio)
+{
+ eax_validate_range<EaxFxSlotException>(
+ "Occlusion LF Ratio",
+ eax_occlusion_lf_ratio,
+ EAXFXSLOT_MINOCCLUSIONLFRATIO,
+ EAXFXSLOT_MAXOCCLUSIONLFRATIO);
+}
+
+void ALeffectslot::eax_validate_fx_slot_all(
+ const EAX40FXSLOTPROPERTIES& fx_slot,
+ int eax_version)
+{
+ eax_validate_fx_slot_effect(fx_slot.guidLoadEffect);
+ eax_validate_fx_slot_volume(fx_slot.lVolume);
+ eax_validate_fx_slot_lock(fx_slot.lLock);
+ eax_validate_fx_slot_flags(fx_slot.ulFlags, eax_version);
+}
+
+void ALeffectslot::eax_validate_fx_slot_all(
+ const EAX50FXSLOTPROPERTIES& fx_slot,
+ int eax_version)
+{
+ eax_validate_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES&>(fx_slot), eax_version);
+
+ eax_validate_fx_slot_occlusion(fx_slot.lOcclusion);
+ eax_validate_fx_slot_occlusion_lf_ratio(fx_slot.flOcclusionLFRatio);
+}
+
+void ALeffectslot::eax_set_fx_slot_effect(
+ const GUID& eax_effect_id)
+{
+ if (eax_eax_fx_slot_.guidLoadEffect == eax_effect_id)
+ {
+ return;
+ }
+
+ eax_eax_fx_slot_.guidLoadEffect = eax_effect_id;
+
+ eax_set_fx_slot_effect();
+}
+
+void ALeffectslot::eax_set_fx_slot_volume(
+ long eax_volume)
+{
+ if (eax_eax_fx_slot_.lVolume == eax_volume)
+ {
+ return;
+ }
+
+ eax_eax_fx_slot_.lVolume = eax_volume;
+
+ eax_set_fx_slot_volume();
+}
+
+void ALeffectslot::eax_set_fx_slot_lock(
+ long eax_lock)
+{
+ if (eax_eax_fx_slot_.lLock == eax_lock)
+ {
+ return;
+ }
+
+ eax_eax_fx_slot_.lLock = eax_lock;
+}
+
+void ALeffectslot::eax_set_fx_slot_flags(
+ unsigned long eax_flags)
+{
+ if (eax_eax_fx_slot_.ulFlags == eax_flags)
+ {
+ return;
+ }
+
+ eax_eax_fx_slot_.ulFlags = eax_flags;
+
+ eax_set_fx_slot_flags();
+}
+
+// [[nodiscard]]
+bool ALeffectslot::eax_set_fx_slot_occlusion(
+ long eax_occlusion)
+{
+ if (eax_eax_fx_slot_.lOcclusion == eax_occlusion)
+ {
+ return false;
+ }
+
+ eax_eax_fx_slot_.lOcclusion = eax_occlusion;
+
+ return true;
+}
+
+// [[nodiscard]]
+bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
+ float eax_occlusion_lf_ratio)
+{
+ if (eax_eax_fx_slot_.flOcclusionLFRatio == eax_occlusion_lf_ratio)
+ {
+ return false;
+ }
+
+ eax_eax_fx_slot_.flOcclusionLFRatio = eax_occlusion_lf_ratio;
+
+ return true;
+}
+
+void ALeffectslot::eax_set_fx_slot_all(
+ const EAX40FXSLOTPROPERTIES& eax_fx_slot)
+{
+ eax_set_fx_slot_effect(eax_fx_slot.guidLoadEffect);
+ eax_set_fx_slot_volume(eax_fx_slot.lVolume);
+ eax_set_fx_slot_lock(eax_fx_slot.lLock);
+ eax_set_fx_slot_flags(eax_fx_slot.ulFlags);
+}
+
+// [[nodiscard]]
+bool ALeffectslot::eax_set_fx_slot_all(
+ const EAX50FXSLOTPROPERTIES& eax_fx_slot)
+{
+ eax_set_fx_slot_all(static_cast<const EAX40FXSLOTPROPERTIES&>(eax_fx_slot));
+
+ const auto is_occlusion_modified = eax_set_fx_slot_occlusion(eax_fx_slot.lOcclusion);
+ const auto is_occlusion_lf_ratio_modified = eax_set_fx_slot_occlusion_lf_ratio(eax_fx_slot.flOcclusionLFRatio);
+
+ return is_occlusion_modified || is_occlusion_lf_ratio_modified;
+}
+
+// [[nodiscard]]
+bool ALeffectslot::eax_dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? eax_get(eax_call) : eax_set(eax_call);
+}
+
+[[noreturn]]
+void ALeffectslot::eax_fail(
+ const char* message)
+{
+ throw EaxFxSlotException{message};
+}
+
+void ALeffectslot::eax_set_eax_fx_slot_defaults()
+{
+ eax_eax_fx_slot_.guidLoadEffect = EAX_NULL_GUID;
+ eax_eax_fx_slot_.lVolume = EAXFXSLOT_DEFAULTVOLUME;
+ eax_eax_fx_slot_.lLock = EAXFXSLOT_UNLOCKED;
+ eax_eax_fx_slot_.ulFlags = EAX50FXSLOT_DEFAULTFLAGS;
+ eax_eax_fx_slot_.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
+ eax_eax_fx_slot_.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
+}
+
+void ALeffectslot::eax_initialize_eax()
+{
+ eax_set_eax_fx_slot_defaults();
+}
+
+void ALeffectslot::eax_initialize_effects()
+{
+ eax_set_fx_slot_effect();
+}
+
+void ALeffectslot::eax_set_default_slot_0_defaults()
+{
+ eax_set_fx_slot_effect(EAX_REVERB_EFFECT);
+}
+
+void ALeffectslot::eax_set_default_slot_1_defaults()
+{
+ eax_set_fx_slot_effect(EAX_CHORUS_EFFECT);
+}
+
+void ALeffectslot::eax_set_default_slots_defaults()
+{
+ switch (eax_fx_slot_index_)
+ {
+ case 0:
+ eax_set_default_slot_0_defaults();
+ break;
+
+ case 1:
+ eax_set_default_slot_1_defaults();
+ break;
+
+ case 2:
+ case 3:
+ break;
+
+ default:
+ eax_fail("FX slot index out of range.");
+ }
+}
+
+void ALeffectslot::eax_get_fx_slot_all(
+ const EaxEaxCall& eax_call) const
+{
+ switch (eax_call.get_version())
+ {
+ case 4:
+ eax_call.set_value<EaxFxSlotException, EAX40FXSLOTPROPERTIES>(eax_eax_fx_slot_);
+ break;
+
+ case 5:
+ eax_call.set_value<EaxFxSlotException, EAX50FXSLOTPROPERTIES>(eax_eax_fx_slot_);
+ break;
+
+ default:
+ eax_fail("Unsupported EAX version.");
+ }
+}
+
+void ALeffectslot::eax_get_fx_slot(
+ const EaxEaxCall& eax_call) const
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXFXSLOT_ALLPARAMETERS:
+ eax_get_fx_slot_all(eax_call);
+ break;
+
+ case EAXFXSLOT_LOADEFFECT:
+ eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.guidLoadEffect);
+ break;
+
+ case EAXFXSLOT_VOLUME:
+ eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lVolume);
+ break;
+
+ case EAXFXSLOT_LOCK:
+ eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lLock);
+ break;
+
+ case EAXFXSLOT_FLAGS:
+ eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.ulFlags);
+ break;
+
+ case EAXFXSLOT_OCCLUSION:
+ eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.lOcclusion);
+ break;
+
+ case EAXFXSLOT_OCCLUSIONLFRATIO:
+ eax_call.set_value<EaxFxSlotException>(eax_eax_fx_slot_.flOcclusionLFRatio);
+ break;
+
+ default:
+ eax_fail("Unsupported FX slot property id.");
+ }
+}
+
+// [[nodiscard]]
+bool ALeffectslot::eax_get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_set_id())
+ {
+ case EaxEaxCallPropertySetId::fx_slot:
+ eax_get_fx_slot(eax_call);
+ break;
+
+ case EaxEaxCallPropertySetId::fx_slot_effect:
+ eax_dispatch_effect(eax_call);
+ break;
+
+ default:
+ eax_fail("Unsupported property id.");
+ }
+
+ return false;
+}
+
+void ALeffectslot::eax_set_fx_slot_effect(
+ ALenum al_effect_type)
+{
+ if (!eax_al_effect_)
+ {
+ eax_al_effect_ = eax_create_al_effect(*eax_al_context_, al_effect_type);
+ }
+ else
+ {
+ auto& device = eax_al_context_->mALDevice;
+ std::lock_guard<std::mutex> effect_lock{device->EffectLock};
+
+ eax_al_effect_->eax_al_set_effect(al_effect_type);
+ }
+
+ eax_al_effect_->eax_initialize();
+
+ eax_set_effect_slot_effect(*eax_al_effect_);
+}
+
+void ALeffectslot::eax_set_fx_slot_effect()
+{
+ auto al_effect_type = ALenum{};
+
+ if (false)
+ {
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_NULL_GUID)
+ {
+ al_effect_type = AL_EFFECT_NULL;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AUTOWAH_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_AUTOWAH;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_CHORUS_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_CHORUS;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_AGCCOMPRESSOR_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_COMPRESSOR;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_DISTORTION_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_DISTORTION;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_REVERB_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_EAXREVERB;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_ECHO_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_ECHO;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_EQUALIZER_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_EQUALIZER;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FLANGER_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_FLANGER;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_FREQUENCYSHIFTER_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_FREQUENCY_SHIFTER;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_PITCHSHIFTER_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_PITCH_SHIFTER;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_RINGMODULATOR_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_RING_MODULATOR;
+ }
+ else if (eax_eax_fx_slot_.guidLoadEffect == EAX_VOCALMORPHER_EFFECT)
+ {
+ al_effect_type = AL_EFFECT_VOCAL_MORPHER;
+ }
+ else
+ {
+ eax_fail("Unsupported effect.");
+ }
+
+ eax_set_fx_slot_effect(al_effect_type);
+}
+
+void ALeffectslot::eax_set_efx_effect_slot_gain()
+{
+ const auto gain = level_mb_to_gain(
+ static_cast<float>(clamp(
+ eax_eax_fx_slot_.lVolume,
+ EAXFXSLOT_MINVOLUME,
+ EAXFXSLOT_MAXVOLUME)));
+
+ eax_set_effect_slot_gain(gain);
+}
+
+void ALeffectslot::eax_set_fx_slot_volume()
+{
+ eax_set_efx_effect_slot_gain();
+}
+
+void ALeffectslot::eax_set_effect_slot_send_auto()
+{
+ eax_set_effect_slot_send_auto((eax_eax_fx_slot_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0);
+}
+
+void ALeffectslot::eax_set_fx_slot_flags()
+{
+ eax_set_effect_slot_send_auto();
+}
+
+void ALeffectslot::eax_set_fx_slot_effect(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eax_effect_id =
+ eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::guidLoadEffect)>();
+
+ eax_validate_fx_slot_effect(eax_effect_id);
+ eax_validate_fx_slot_lock_state(eax_eax_fx_slot_.lLock, eax_effect_id);
+
+ eax_set_fx_slot_effect(eax_effect_id);
+}
+
+void ALeffectslot::eax_set_fx_slot_volume(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eax_volume =
+ eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lVolume)>();
+
+ eax_validate_fx_slot_volume(eax_volume);
+ eax_set_fx_slot_volume(eax_volume);
+}
+
+void ALeffectslot::eax_set_fx_slot_lock(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eax_lock =
+ eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::lLock)>();
+
+ eax_validate_fx_slot_lock(eax_lock);
+ eax_set_fx_slot_lock(eax_lock);
+}
+
+void ALeffectslot::eax_set_fx_slot_flags(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eax_flags =
+ eax_call.get_value<EaxFxSlotException, const decltype(EAX40FXSLOTPROPERTIES::ulFlags)>();
+
+ eax_validate_fx_slot_flags(eax_flags, eax_call.get_version());
+ eax_set_fx_slot_flags(eax_flags);
+}
+
+// [[nodiscard]]
+bool ALeffectslot::eax_set_fx_slot_occlusion(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eax_occlusion =
+ eax_call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::lOcclusion)>();
+
+ eax_validate_fx_slot_occlusion(eax_occlusion);
+
+ return eax_set_fx_slot_occlusion(eax_occlusion);
+}
+
+// [[nodiscard]]
+bool ALeffectslot::eax_set_fx_slot_occlusion_lf_ratio(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eax_occlusion_lf_ratio =
+ eax_call.get_value<EaxFxSlotException, const decltype(EAX50FXSLOTPROPERTIES::flOcclusionLFRatio)>();
+
+ eax_validate_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
+
+ return eax_set_fx_slot_occlusion_lf_ratio(eax_occlusion_lf_ratio);
+}
+
+// [[nodiscard]]
+bool ALeffectslot::eax_set_fx_slot_all(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_version())
+ {
+ case 4:
+ {
+ const auto& eax_all =
+ eax_call.get_value<EaxFxSlotException, const EAX40FXSLOTPROPERTIES>();
+
+ eax_validate_fx_slot_all(eax_all, eax_call.get_version());
+ eax_set_fx_slot_all(eax_all);
+
+ return false;
+ }
+
+ case 5:
+ {
+ const auto& eax_all =
+ eax_call.get_value<EaxFxSlotException, const EAX50FXSLOTPROPERTIES>();
+
+ eax_validate_fx_slot_all(eax_all, eax_call.get_version());
+ return eax_set_fx_slot_all(eax_all);
+ }
+
+ default:
+ eax_fail("Unsupported EAX version.");
+ }
+}
+
+bool ALeffectslot::eax_set_fx_slot(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXFXSLOT_NONE:
+ return false;
+
+ case EAXFXSLOT_ALLPARAMETERS:
+ return eax_set_fx_slot_all(eax_call);
+
+ case EAXFXSLOT_LOADEFFECT:
+ eax_set_fx_slot_effect(eax_call);
+ return false;
+
+ case EAXFXSLOT_VOLUME:
+ eax_set_fx_slot_volume(eax_call);
+ return false;
+
+ case EAXFXSLOT_LOCK:
+ eax_set_fx_slot_lock(eax_call);
+ return false;
+
+ case EAXFXSLOT_FLAGS:
+ eax_set_fx_slot_flags(eax_call);
+ return false;
+
+ case EAXFXSLOT_OCCLUSION:
+ return eax_set_fx_slot_occlusion(eax_call);
+
+ case EAXFXSLOT_OCCLUSIONLFRATIO:
+ return eax_set_fx_slot_occlusion_lf_ratio(eax_call);
+
+
+ default:
+ eax_fail("Unsupported FX slot property id.");
+ }
+}
+
+// [[nodiscard]]
+bool ALeffectslot::eax_set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_set_id())
+ {
+ case EaxEaxCallPropertySetId::fx_slot:
+ return eax_set_fx_slot(eax_call);
+
+ case EaxEaxCallPropertySetId::fx_slot_effect:
+ eax_dispatch_effect(eax_call);
+ return false;
+
+ default:
+ eax_fail("Unsupported property id.");
+ }
+}
+
+void ALeffectslot::eax_dispatch_effect(
+ const EaxEaxCall& eax_call)
+{
+ auto is_changed = false;
+
+ {
+ std::lock_guard<std::mutex> effect_lock{eax_al_context_->mALDevice->EffectLock};
+
+ if (!eax_al_effect_->eax_effect)
+ {
+ return;
+ }
+
+ is_changed = eax_al_effect_->eax_effect->dispatch(eax_call);
+ }
+
+ if (is_changed)
+ {
+ eax_set_effect_slot_effect(*eax_al_effect_);
+ }
+}
+
+void ALeffectslot::eax_set_effect_slot_effect(
+ ALeffect& effect)
+{
+#define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
+
+ auto& device = *eax_al_context_->mALDevice;
+
+ std::lock_guard<std::mutex> effect_slot_lock{eax_al_context_->mEffectSlotLock};
+ std::lock_guard<std::mutex> effect_lock{device.EffectLock};
+
+ const auto error = initEffect(&effect, eax_al_context_);
+
+ if (error != AL_NO_ERROR)
+ {
+ ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
+ return;
+ }
+
+ if (mState == SlotState::Initial)
+ {
+ mPropsDirty.test_and_clear(std::memory_order_acq_rel);
+ updateProps(eax_al_context_);
+
+ auto effect_slot_ptr = this;
+
+ AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
+ mState = SlotState::Playing;
+
+ return;
+ }
+
+ {
+ auto context = EaxAlContextWrapper{*eax_al_context_};
+ auto slot = this;
+
+ DO_UPDATEPROPS();
+ }
+
+#undef EAX_PREFIX
+}
+
+void ALeffectslot::eax_set_effect_slot_send_auto(
+ bool is_send_auto)
+{
+ std::lock_guard<std::mutex> effect_slot_lock{eax_al_context_->mEffectSlotLock};
+
+ const auto is_changed = (AuxSendAuto != is_send_auto);
+
+ AuxSendAuto = is_send_auto;
+
+ if (is_changed)
+ {
+ auto context = EaxAlContextWrapper{*eax_al_context_};
+ auto slot = this;
+
+ DO_UPDATEPROPS();
+ }
+}
+
+void ALeffectslot::eax_set_effect_slot_gain(
+ ALfloat gain)
+{
+#define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_GAIN] "
+
+ if (gain < 0.0F || gain > 1.0F)
+ {
+ ERR(EAX_PREFIX "%s\n", "Gain out of range.");
+ return;
+ }
+
+ std::lock_guard<std::mutex> effect_slot_lock{eax_al_context_->mEffectSlotLock};
+
+ const auto is_changed = (Gain != gain);
+
+ Gain = gain;
+
+ if (is_changed)
+ {
+ auto context = EaxAlContextWrapper{*eax_al_context_};
+ auto slot = this;
+
+ DO_UPDATEPROPS();
+ }
+
+#undef EAX_PREFIX
+}
+
+
+EaxAlEffectSlotDeleter::EaxAlEffectSlotDeleter(
+ ALCcontext& context) noexcept
+ :
+ context_{&context}
+{
+}
+
+void EaxAlEffectSlotDeleter::operator()(
+ ALeffectslot* effect_slot)
+{
+ assert(effect_slot);
+ eax_delete_al_effect_slot(*context_, *effect_slot);
+}
+
+
+EaxAlEffectSlotUPtr eax_create_al_effect_slot(
+ ALCcontext& context)
+{
+#define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] "
+
+ std::unique_lock<std::mutex> effect_slot_lock{context.mEffectSlotLock};
+
+ auto& device = *context.mALDevice;
+
+ if (context.mNumEffectSlots == device.AuxiliaryEffectSlotMax)
+ {
+ ERR(EAX_PREFIX "%s\n", "Out of memory.");
+ return nullptr;
+ }
+
+ if (!EnsureEffectSlots(&context, 1))
+ {
+ ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
+ return nullptr;
+ }
+
+ auto effect_slot = EaxAlEffectSlotUPtr{AllocEffectSlot(&context), EaxAlEffectSlotDeleter{context}};
+
+ if (!effect_slot)
+ {
+ ERR(EAX_PREFIX "%s\n", "Failed to allocate.");
+ return nullptr;
+ }
+
+ return effect_slot;
+
+#undef EAX_PREFIX
+}
+
+void eax_delete_al_effect_slot(
+ ALCcontext& context,
+ ALeffectslot& effect_slot)
+{
+#define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] "
+
+ std::lock_guard<std::mutex> effect_slot_lock{context.mEffectSlotLock};
+
+ if (ReadRef(effect_slot.ref) != 0)
+ {
+ ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
+ return;
+ }
+
+ auto effect_slot_ptr = &effect_slot;
+
+ RemoveActiveEffectSlots({&effect_slot_ptr, 1}, &context);
+ FreeEffectSlot(&context, &effect_slot);
+
+#undef EAX_PREFIX
+}
+#endif // ALSOFT_EAX