diff options
author | Boris I. Bendovsky <[email protected]> | 2022-06-18 23:36:42 +0300 |
---|---|---|
committer | GitHub <[email protected]> | 2022-06-18 13:36:42 -0700 |
commit | d21ff67554d494ec53dcb4747b828478f8d5690a (patch) | |
tree | 5a8c39eeba176e4cfd539a9548af4bcc41557d5b /al | |
parent | 0e7d5736c0b66d0639beb395b512416d252d28fb (diff) |
[EAX] Add separate source state for each version (#720)
Diffstat (limited to 'al')
-rw-r--r-- | al/eax/api.cpp | 2 | ||||
-rw-r--r-- | al/eax/api.h | 19 | ||||
-rw-r--r-- | al/eax/call.h | 17 | ||||
-rw-r--r-- | al/eax/utils.h | 69 | ||||
-rw-r--r-- | al/source.cpp | 2782 | ||||
-rw-r--r-- | al/source.h | 1333 |
6 files changed, 1656 insertions, 2566 deletions
diff --git a/al/eax/api.cpp b/al/eax/api.cpp index 34ba554f..a1e9d3e1 100644 --- a/al/eax/api.cpp +++ b/al/eax/api.cpp @@ -313,7 +313,7 @@ bool operator==( lhs.flOcclusionLFRatio == rhs.flOcclusionLFRatio; } -const EAX50ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS +const EAX40ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID = EAX40ACTIVEFXSLOTS {{ EAX_NULL_GUID, EAXPROPERTYID_EAX40_FXSlot0, diff --git a/al/eax/api.h b/al/eax/api.h index f4419ddb..a9b0a50f 100644 --- a/al/eax/api.h +++ b/al/eax/api.h @@ -275,6 +275,10 @@ struct EAX20BUFFERPROPERTIES unsigned long dwFlags; // modifies the behavior of properties }; // EAX20BUFFERPROPERTIES +inline bool operator==(const EAX20BUFFERPROPERTIES& lhs, const EAX20BUFFERPROPERTIES& rhs) noexcept +{ + return std::memcmp(&lhs, &rhs, sizeof(EAX20BUFFERPROPERTIES)) == 0; +} extern const GUID DSPROPSETID_EAX30_ListenerProperties; @@ -707,12 +711,21 @@ struct EAX30SOURCEPROPERTIES unsigned long ulFlags; // modifies the behavior of properties }; // EAX30SOURCEPROPERTIES -struct EAX50SOURCEPROPERTIES : - public EAX30SOURCEPROPERTIES +inline bool operator==(const EAX30SOURCEPROPERTIES& lhs, const EAX30SOURCEPROPERTIES& rhs) noexcept +{ + return std::memcmp(&lhs, &rhs, sizeof(EAX30SOURCEPROPERTIES)) == 0; +} + +struct EAX50SOURCEPROPERTIES : public EAX30SOURCEPROPERTIES { float flMacroFXFactor; }; // EAX50SOURCEPROPERTIES +inline bool operator==(const EAX50SOURCEPROPERTIES& lhs, const EAX50SOURCEPROPERTIES& rhs) noexcept +{ + return std::memcmp(&lhs, &rhs, sizeof(EAX50SOURCEPROPERTIES)) == 0; +} + struct EAXSOURCEALLSENDPROPERTIES { GUID guidReceivingFXSlotID; @@ -808,7 +821,7 @@ struct EAXSOURCEEXCLUSIONSENDPROPERTIES float flExclusionLFRatio; }; // EAXSOURCEEXCLUSIONSENDPROPERTIES -extern const EAX50ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID; +extern const EAX40ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID; extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID; diff --git a/al/eax/call.h b/al/eax/call.h index d491d6f9..9c2706c3 100644 --- a/al/eax/call.h +++ b/al/eax/call.h @@ -2,6 +2,7 @@ #define EAX_EAX_CALL_INCLUDED #include "AL/al.h" +#include "alnumeric.h" #include "alspan.h" #include "api.h" #include "fx_slot_index.h" @@ -48,18 +49,22 @@ public: return *static_cast<TValue*>(property_buffer_); } - template<typename TException, typename TValue> - al::span<TValue> get_values() const + template<typename TValue> + al::span<TValue> get_values(size_t max_count) const { - if (property_size_ < static_cast<ALuint>(sizeof(TValue))) - { + if (max_count == 0 || property_size_ < static_cast<ALuint>(sizeof(TValue))) fail_too_small(); - } - const auto count = property_size_ / sizeof(TValue); + const auto count = minz(property_size_ / sizeof(TValue), max_count); return al::span<TValue>{static_cast<TValue*>(property_buffer_), count}; } + template<typename TValue> + al::span<TValue> get_values() const + { + return get_values<TValue>(~size_t{}); + } + template<typename TException, typename TValue> void set_value(const TValue& value) const { diff --git a/al/eax/utils.h b/al/eax/utils.h index d3d4a196..5a8fdd64 100644 --- a/al/eax/utils.h +++ b/al/eax/utils.h @@ -6,22 +6,14 @@ #include <string> #include <type_traits> - -struct EaxAlLowPassParam -{ +struct EaxAlLowPassParam { float gain; float gain_hf; -}; // EaxAlLowPassParam - +}; -void eax_log_exception( - const char* message = nullptr) noexcept; +void eax_log_exception(const char* message = nullptr) noexcept; - -template< - typename TException, - typename TValue -> +template<typename TException, typename TValue> void eax_validate_range( const char* value_name, const TValue& value, @@ -29,9 +21,7 @@ void eax_validate_range( const TValue& max_value) { if (value >= min_value && value <= max_value) - { return; - } const auto message = std::string{value_name} + @@ -43,60 +33,38 @@ void eax_validate_range( throw TException{message.c_str()}; } +namespace detail { -namespace detail -{ - - -template< - typename T -> -struct EaxIsBitFieldStruct -{ +template<typename T> +struct EaxIsBitFieldStruct { private: using yes = std::true_type; using no = std::false_type; - template< - typename U - > + template<typename U> static auto test(int) -> decltype(std::declval<typename U::EaxIsBitFieldStruct>(), yes{}); - template< - typename - > + template<typename> static no test(...); - public: static constexpr auto value = std::is_same<decltype(test<T>(0)), yes>::value; -}; // EaxIsBitFieldStruct - +}; -template< - typename T, - typename TValue -> -inline bool eax_bit_fields_are_equal( - const T& lhs, - const T& rhs) noexcept +template<typename T, typename TValue> +inline bool eax_bit_fields_are_equal(const T& lhs, const T& rhs) noexcept { static_assert(sizeof(T) == sizeof(TValue), "Invalid type size."); - return reinterpret_cast<const TValue&>(lhs) == reinterpret_cast<const TValue&>(rhs); } - } // namespace detail - template< typename T, std::enable_if_t<detail::EaxIsBitFieldStruct<T>::value, int> = 0 > -inline bool operator==( - const T& lhs, - const T& rhs) noexcept +inline bool operator==(const T& lhs, const T& rhs) noexcept { using Value = std::conditional_t< sizeof(T) == 1, @@ -107,13 +75,9 @@ inline bool operator==( std::conditional_t< sizeof(T) == 4, std::uint32_t, - void - > - > - >; + void>>>; static_assert(!std::is_same<Value, void>::value, "Unsupported type."); - return detail::eax_bit_fields_are_equal<T, Value>(lhs, rhs); } @@ -121,12 +85,9 @@ template< typename T, std::enable_if_t<detail::EaxIsBitFieldStruct<T>::value, int> = 0 > -inline bool operator!=( - const T& lhs, - const T& rhs) noexcept +inline bool operator!=(const T& lhs, const T& rhs) noexcept { return !(lhs == rhs); } - #endif // !EAX_UTILS_INCLUDED diff --git a/al/source.cpp b/al/source.cpp index f2c7e2f8..475f3cf8 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -73,7 +73,7 @@ #include "threads.h" #ifdef ALSOFT_EAX -#include "eax/exception.h" +#include <cassert> #endif // ALSOFT_EAX namespace { @@ -3640,13 +3640,7 @@ void UpdateAllSourceProps(ALCcontext *context) usemask &= ~(1_u64 << idx); ALsource *source{sublist.Sources + idx}; - source->eax_commit(); - - if(Voice *voice{GetSourceVoice(source, context)}) - { - if(std::exchange(source->mPropsDirty, false)) - UpdateSourceProps(source, voice, context); - } + source->eax_commit_and_update(); } } } @@ -3685,45 +3679,6 @@ SourceSubList::~SourceSubList() #ifdef ALSOFT_EAX -class EaxSourceException : - public EaxException -{ -public: - explicit EaxSourceException( - const char* message) - : - EaxException{"EAX_SOURCE", message} - { - } -}; // EaxSourceException - - -class EaxSourceActiveFxSlotsException : - public EaxException -{ -public: - explicit EaxSourceActiveFxSlotsException( - const char* message) - : - EaxException{"EAX_SOURCE_ACTIVE_FX_SLOTS", message} - { - } -}; // EaxSourceActiveFxSlotsException - - -class EaxSourceSendException : - public EaxException -{ -public: - explicit EaxSourceSendException( - const char* message) - : - EaxException{"EAX_SOURCE_SEND", message} - { - } -}; // EaxSourceSendException - - void EaxUpdateSourceVoice(ALsource *source, ALCcontext *context) { if(Voice *voice{GetSourceVoice(source, context)}) @@ -3733,2350 +3688,1158 @@ void EaxUpdateSourceVoice(ALsource *source, ALCcontext *context) } } +constexpr const ALsource::EaxFxSlotIds ALsource::eax4_fx_slot_ids; +constexpr const ALsource::EaxFxSlotIds ALsource::eax5_fx_slot_ids; void ALsource::eax_initialize(ALCcontext *context) noexcept { - assert(context); + assert(context != nullptr); eax_al_context_ = context; + eax_primary_fx_slot_id_ = eax_al_context_->eax_get_primary_fx_slot_index(); + eax_version_ = eax_al_context_->eax_get_version(); eax_set_defaults(); - eax_initialize_fx_slots(); - - eax_d_ = eax_; + eax_commit(EaxCommitType::forced); } -void ALsource::eax_update_filters() +void ALsource::eax_dispatch(const EaxCall& call) { - eax_update_filters_internal(); + const auto eax_version = call.get_version(); + eax_is_version_changed_ |= (eax_version_ == eax_version); + eax_version_ = eax_version; + call.is_get() ? eax_get(call) : eax_set(call); } -void ALsource::eax_update(EaxContextSharedDirtyFlags) +void ALsource::eax_commit() { - /* NOTE: EaxContextSharedDirtyFlags only has one flag (primary_fx_slot_id), - * which must be true for this to be called. - */ - if(eax_uses_primary_id_) - eax_update_primary_fx_slot_id(); + eax_commit(EaxCommitType::normal); } void ALsource::eax_commit_and_update() { - eax_apply_deferred(); + eax_commit(); EaxUpdateSourceVoice(this, eax_al_context_); } -ALsource* ALsource::eax_lookup_source( - ALCcontext& al_context, - ALuint source_id) noexcept +ALsource* ALsource::eax_lookup_source(ALCcontext& al_context, ALuint source_id) noexcept { return LookupSource(&al_context, source_id); } -[[noreturn]] -void ALsource::eax_fail( - const char* message) -{ - throw EaxSourceException{message}; -} - -void ALsource::eax_set_source_defaults() noexcept +[[noreturn]] void ALsource::eax_fail(const char* message) { - eax1_.fMix = EAX_REVERBMIX_USEDISTANCE; - - eax_.source.lDirect = EAXSOURCE_DEFAULTDIRECT; - eax_.source.lDirectHF = EAXSOURCE_DEFAULTDIRECTHF; - eax_.source.lRoom = EAXSOURCE_DEFAULTROOM; - eax_.source.lRoomHF = EAXSOURCE_DEFAULTROOMHF; - eax_.source.lObstruction = EAXSOURCE_DEFAULTOBSTRUCTION; - eax_.source.flObstructionLFRatio = EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO; - eax_.source.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION; - eax_.source.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO; - eax_.source.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO; - eax_.source.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO; - eax_.source.lExclusion = EAXSOURCE_DEFAULTEXCLUSION; - eax_.source.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO; - eax_.source.lOutsideVolumeHF = EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF; - eax_.source.flDopplerFactor = EAXSOURCE_DEFAULTDOPPLERFACTOR; - eax_.source.flRolloffFactor = EAXSOURCE_DEFAULTROLLOFFFACTOR; - eax_.source.flRoomRolloffFactor = EAXSOURCE_DEFAULTROOMROLLOFFFACTOR; - eax_.source.flAirAbsorptionFactor = EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR; - eax_.source.ulFlags = EAXSOURCE_DEFAULTFLAGS; - eax_.source.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR; + throw Exception{message}; } -void ALsource::eax_set_active_fx_slots_defaults() noexcept +[[noreturn]] void ALsource::eax_fail_unknown_property_id() { - eax_.active_fx_slots = EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID; + eax_fail("Unknown property id."); } -void ALsource::eax_set_send_defaults(EAXSOURCEALLSENDPROPERTIES& eax_send) noexcept +[[noreturn]] void ALsource::eax_fail_unknown_version() { - eax_send.guidReceivingFXSlotID = EAX_NULL_GUID; - eax_send.lSend = EAXSOURCE_DEFAULTSEND; - eax_send.lSendHF = EAXSOURCE_DEFAULTSENDHF; - eax_send.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION; - eax_send.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO; - eax_send.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO; - eax_send.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO; - eax_send.lExclusion = EAXSOURCE_DEFAULTEXCLUSION; - eax_send.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO; + eax_fail("Unknown version."); } -void ALsource::eax_set_sends_defaults() noexcept +[[noreturn]] void ALsource::eax_fail_unknown_active_fx_slot_id() { - for (auto& eax_send : eax_.sends) - { - eax_set_send_defaults(eax_send); - } + eax_fail("Unknown active FX slot ID."); } -void ALsource::eax_set_speaker_levels_defaults() noexcept +[[noreturn]] void ALsource::eax_fail_unknown_receiving_fx_slot_id() { - std::fill(eax_.speaker_levels.begin(), eax_.speaker_levels.end(), EAXSOURCE_DEFAULTSPEAKERLEVEL); + eax_fail("Unknown receiving FX slot ID."); } -void ALsource::eax_set_defaults() noexcept +void ALsource::eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept { - eax_set_source_defaults(); - eax_set_active_fx_slots_defaults(); - eax_set_sends_defaults(); - eax_set_speaker_levels_defaults(); -} - -float ALsource::eax_calculate_dst_occlusion_mb( - long src_occlusion_mb, - float path_ratio, - float lf_ratio) noexcept -{ - const auto ratio_1 = path_ratio + lf_ratio - 1.0F; - const auto ratio_2 = path_ratio * lf_ratio; - const auto ratio = (ratio_2 > ratio_1) ? ratio_2 : ratio_1; - const auto dst_occlustion_mb = static_cast<float>(src_occlusion_mb) * ratio; - - return dst_occlustion_mb; -} - -EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept -{ - auto gain_mb = - static_cast<float>(eax_.source.lDirect) + - - (static_cast<float>(eax_.source.lObstruction) * eax_.source.flObstructionLFRatio) + - - eax_calculate_dst_occlusion_mb( - eax_.source.lOcclusion, - eax_.source.flOcclusionDirectRatio, - eax_.source.flOcclusionLFRatio); - - auto gain_hf_mb = - static_cast<float>(eax_.source.lDirectHF) + - - static_cast<float>(eax_.source.lObstruction) + - - (static_cast<float>(eax_.source.lOcclusion) * eax_.source.flOcclusionDirectRatio); - - for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i) - { - if (eax_active_fx_slots_[i]) - { - const auto& send = eax_.sends[i]; - - gain_mb += eax_calculate_dst_occlusion_mb( - send.lOcclusion, - send.flOcclusionDirectRatio, - send.flOcclusionLFRatio); - - gain_hf_mb += static_cast<float>(send.lOcclusion) * send.flOcclusionDirectRatio; - } + for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) { + auto& send = sends[i]; + send.guidReceivingFXSlotID = *(ids[i]); + send.lSend = EAXSOURCE_DEFAULTSEND; + send.lSendHF = EAXSOURCE_DEFAULTSENDHF; + send.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION; + send.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO; + send.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO; + send.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO; + send.lExclusion = EAXSOURCE_DEFAULTEXCLUSION; + send.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO; } - - const auto al_low_pass_param = EaxAlLowPassParam - { - level_mb_to_gain(gain_mb), - minf(level_mb_to_gain(gain_hf_mb), 1.0f) - }; - - return al_low_pass_param; } -EaxAlLowPassParam ALsource::eax_create_room_filter_param( - const ALeffectslot& fx_slot, - const EAXSOURCEALLSENDPROPERTIES& send) const noexcept +void ALsource::eax1_set_defaults(Eax1Props& props) noexcept { - const auto& fx_slot_eax = fx_slot.eax_get_eax_fx_slot(); - - const auto gain_mb = - static_cast<float>( - eax_.source.lRoom + - send.lSend) + - - eax_calculate_dst_occlusion_mb( - eax_.source.lOcclusion, - eax_.source.flOcclusionRoomRatio, - eax_.source.flOcclusionLFRatio - ) + - - eax_calculate_dst_occlusion_mb( - send.lOcclusion, - send.flOcclusionRoomRatio, - send.flOcclusionLFRatio - ) + - - (static_cast<float>(eax_.source.lExclusion) * eax_.source.flExclusionLFRatio) + - (static_cast<float>(send.lExclusion) * send.flExclusionLFRatio) + - - 0.0F; - - const auto gain_hf_mb = - static_cast<float>( - eax_.source.lRoomHF + - send.lSendHF) + - - (static_cast<float>(fx_slot_eax.lOcclusion + eax_.source.lOcclusion) * eax_.source.flOcclusionRoomRatio) + - (static_cast<float>(send.lOcclusion) * send.flOcclusionRoomRatio) + - - static_cast<float>( - eax_.source.lExclusion + - send.lExclusion) + - - 0.0F; - - const auto al_low_pass_param = EaxAlLowPassParam - { - level_mb_to_gain(gain_mb), - minf(level_mb_to_gain(gain_hf_mb), 1.0f) - }; - - return al_low_pass_param; + props.fMix = EAX_REVERBMIX_USEDISTANCE; } -void ALsource::eax_set_fx_slots() +void ALsource::eax1_set_defaults() noexcept { - eax_uses_primary_id_ = false; - eax_has_active_fx_slots_ = false; - eax_active_fx_slots_.fill(false); - - for(const auto& eax_active_fx_slot_id : eax_.active_fx_slots.guidActiveFXSlots) - { - auto fx_slot_index = EaxFxSlotIndex{}; - - if(eax_active_fx_slot_id == EAX_PrimaryFXSlotID) - { - eax_uses_primary_id_ = true; - fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index(); - } - else - { - fx_slot_index = eax_active_fx_slot_id; - } - - if(fx_slot_index.has_value()) - { - eax_has_active_fx_slots_ = true; - eax_active_fx_slots_[*fx_slot_index] = true; - } - } - - for(auto i = 0u;i < eax_active_fx_slots_.size();++i) - { - if(!eax_active_fx_slots_[i]) - eax_set_al_source_send(nullptr, i, EaxAlLowPassParam{1.0f, 1.0f}); - } + eax1_set_defaults(eax1_.i); + eax1_.d = eax1_.i; } -void ALsource::eax_initialize_fx_slots() +void ALsource::eax2_set_defaults(Eax2Props& props) noexcept { - eax_set_fx_slots(); - eax_update_filters_internal(); + props.lDirect = EAXSOURCE_DEFAULTDIRECT; + props.lDirectHF = EAXSOURCE_DEFAULTDIRECTHF; + props.lRoom = EAXSOURCE_DEFAULTROOM; + props.lRoomHF = EAXSOURCE_DEFAULTROOMHF; + props.flRoomRolloffFactor = EAXSOURCE_DEFAULTROOMROLLOFFFACTOR; + props.lObstruction = EAXSOURCE_DEFAULTOBSTRUCTION; + props.flObstructionLFRatio = EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO; + props.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION; + props.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO; + props.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO; + props.lOutsideVolumeHF = EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF; + props.flAirAbsorptionFactor = EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR; + props.dwFlags = EAXSOURCE_DEFAULTFLAGS; } -void ALsource::eax_update_direct_filter_internal() +void ALsource::eax2_set_defaults() noexcept { - const auto& direct_param = eax_create_direct_filter_param(); - - Direct.Gain = direct_param.gain; - Direct.GainHF = direct_param.gain_hf; - Direct.HFReference = LOWPASSFREQREF; - Direct.GainLF = 1.0f; - Direct.LFReference = HIGHPASSFREQREF; - mPropsDirty = true; + eax2_set_defaults(eax2_.i); + eax2_.d = eax2_.i; } -void ALsource::eax_update_room_filters_internal() +void ALsource::eax3_set_defaults(Eax3Props& props) noexcept { - if (!eax_has_active_fx_slots_) - { - return; - } - - for (auto i = 0u; i < EAX_MAX_FXSLOTS; ++i) - { - if (eax_active_fx_slots_[i]) - { - auto& fx_slot = eax_al_context_->eax_get_fx_slot(static_cast<std::size_t>(i)); - const auto& send = eax_.sends[i]; - const auto& room_param = eax_create_room_filter_param(fx_slot, send); - - eax_set_al_source_send(&fx_slot, i, room_param); - } - } + props.lDirect = EAXSOURCE_DEFAULTDIRECT; + props.lDirectHF = EAXSOURCE_DEFAULTDIRECTHF; + props.lRoom = EAXSOURCE_DEFAULTROOM; + props.lRoomHF = EAXSOURCE_DEFAULTROOMHF; + props.lObstruction = EAXSOURCE_DEFAULTOBSTRUCTION; + props.flObstructionLFRatio = EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO; + props.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION; + props.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO; + props.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO; + props.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO; + props.lExclusion = EAXSOURCE_DEFAULTEXCLUSION; + props.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO; + props.lOutsideVolumeHF = EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF; + props.flDopplerFactor = EAXSOURCE_DEFAULTDOPPLERFACTOR; + props.flRolloffFactor = EAXSOURCE_DEFAULTROLLOFFFACTOR; + props.flRoomRolloffFactor = EAXSOURCE_DEFAULTROOMROLLOFFFACTOR; + props.flAirAbsorptionFactor = EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR; + props.ulFlags = EAXSOURCE_DEFAULTFLAGS; } -void ALsource::eax_update_filters_internal() +void ALsource::eax3_set_defaults() noexcept { - eax_update_direct_filter_internal(); - eax_update_room_filters_internal(); + eax3_set_defaults(eax3_.i); + eax3_.d = eax3_.i; } -void ALsource::eax_update_primary_fx_slot_id() +void ALsource::eax4_set_sends_defaults(EaxSends& sends) noexcept { - const auto& previous_primary_fx_slot_index = eax_al_context_->eax_get_previous_primary_fx_slot_index(); - const auto& primary_fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index(); - - if (previous_primary_fx_slot_index == primary_fx_slot_index) - { - return; - } - - if (previous_primary_fx_slot_index.has_value()) - { - const auto fx_slot_index = previous_primary_fx_slot_index.value(); - eax_active_fx_slots_[fx_slot_index] = false; - - eax_set_al_source_send(nullptr, fx_slot_index, EaxAlLowPassParam{1.0f, 1.0f}); - } - - if (primary_fx_slot_index.has_value()) - { - const auto fx_slot_index = primary_fx_slot_index.value(); - eax_active_fx_slots_[fx_slot_index] = true; - - auto& fx_slot = eax_al_context_->eax_get_fx_slot(fx_slot_index); - const auto& send = eax_.sends[fx_slot_index]; - const auto& room_param = eax_create_room_filter_param(fx_slot, send); - - eax_set_al_source_send(&fx_slot, fx_slot_index, room_param); - } - - eax_has_active_fx_slots_ = std::any_of( - eax_active_fx_slots_.cbegin(), - eax_active_fx_slots_.cend(), - [](const auto& item) - { - return item; - } - ); + eax_set_sends_defaults(sends, eax4_fx_slot_ids); } -void ALsource::eax_defer_active_fx_slots( - const EaxCall& call) +void ALsource::eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept { - const auto active_fx_slots_span = - call.get_values<EaxSourceActiveFxSlotsException, const GUID>(); - - const auto fx_slot_count = active_fx_slots_span.size(); - - if (fx_slot_count <= 0 || fx_slot_count > EAX_MAX_FXSLOTS) - { - throw EaxSourceActiveFxSlotsException{"Count out of range."}; - } - - for (auto i = std::size_t{}; i < fx_slot_count; ++i) - { - const auto& fx_slot_guid = active_fx_slots_span[i]; - - if (fx_slot_guid != EAX_NULL_GUID && - fx_slot_guid != EAX_PrimaryFXSlotID && - fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot0 && - fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot0 && - fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot1 && - fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot1 && - fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot2 && - fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot2 && - fx_slot_guid != EAXPROPERTYID_EAX40_FXSlot3 && - fx_slot_guid != EAXPROPERTYID_EAX50_FXSlot3) - { - throw EaxSourceActiveFxSlotsException{"Unsupported GUID."}; - } - } - - for (auto i = std::size_t{}; i < fx_slot_count; ++i) - { - eax_d_.active_fx_slots.guidActiveFXSlots[i] = active_fx_slots_span[i]; - } - - for (auto i = fx_slot_count; i < EAX_MAX_FXSLOTS; ++i) - { - eax_d_.active_fx_slots.guidActiveFXSlots[i] = EAX_NULL_GUID; - } - - eax_are_active_fx_slots_dirty_ = (eax_d_.active_fx_slots != eax_.active_fx_slots); + slots = EAX40SOURCE_DEFAULTACTIVEFXSLOTID; } - -const char* ALsource::eax_get_exclusion_name() noexcept +void ALsource::eax4_set_defaults() noexcept { - return "Exclusion"; + eax3_set_defaults(eax4_.i.source); + eax4_set_sends_defaults(eax4_.i.sends); + eax4_set_active_fx_slots_defaults(eax4_.i.active_fx_slots); + eax4_.d = eax4_.i; } -const char* ALsource::eax_get_exclusion_lf_ratio_name() noexcept +void ALsource::eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept { - return "Exclusion LF Ratio"; + eax3_set_defaults(static_cast<Eax3Props&>(props)); + props.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR; } -const char* ALsource::eax_get_occlusion_name() noexcept +void ALsource::eax5_set_sends_defaults(EaxSends& sends) noexcept { - return "Occlusion"; + eax_set_sends_defaults(sends, eax5_fx_slot_ids); } -const char* ALsource::eax_get_occlusion_lf_ratio_name() noexcept +void ALsource::eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept { - return "Occlusion LF Ratio"; + slots = EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID; } -const char* ALsource::eax_get_occlusion_direct_ratio_name() noexcept +void ALsource::eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept { - return "Occlusion Direct Ratio"; -} - -const char* ALsource::eax_get_occlusion_room_ratio_name() noexcept -{ - return "Occlusion Room Ratio"; -} - -void ALsource::eax1_validate_reverb_mix(float reverb_mix) -{ - if (reverb_mix == EAX_REVERBMIX_USEDISTANCE) - return; - - eax_validate_range<EaxSourceSendException>("Reverb Mix", reverb_mix, EAX_BUFFER_MINREVERBMIX, EAX_BUFFER_MAXREVERBMIX); -} - -void ALsource::eax_validate_send_receiving_fx_slot_guid( - const GUID& guidReceivingFXSlotID) -{ - if (guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 && - guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 && - guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 && - guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 && - guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 && - guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 && - guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot3 && - guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot3) - { - throw EaxSourceSendException{"Unsupported receiving FX slot GUID."}; + for (auto i = size_t{}; i < eax_max_speakers; ++i) { + auto& speaker_level = speaker_levels[i]; + speaker_level.lSpeakerID = static_cast<long>(EAXSPEAKER_FRONT_LEFT + i); + speaker_level.lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; } } -void ALsource::eax_validate_send_send( - long lSend) +void ALsource::eax5_set_defaults(Eax5Props& props) noexcept { - eax_validate_range<EaxSourceSendException>( - "Send", - lSend, - EAXSOURCE_MINSEND, - EAXSOURCE_MAXSEND); + eax5_set_source_defaults(props.source); + eax5_set_sends_defaults(props.sends); + eax5_set_active_fx_slots_defaults(props.active_fx_slots); + eax5_set_speaker_levels_defaults(props.speaker_levels); } -void ALsource::eax_validate_send_send_hf( - long lSendHF) +void ALsource::eax5_set_defaults() noexcept { - eax_validate_range<EaxSourceSendException>( - "Send HF", - lSendHF, - EAXSOURCE_MINSENDHF, - EAXSOURCE_MAXSENDHF); + eax5_set_defaults(eax5_.i); + eax5_.d = eax5_.i; } -void ALsource::eax_validate_send_occlusion( - long lOcclusion) -{ - eax_validate_range<EaxSourceSendException>( - eax_get_occlusion_name(), - lOcclusion, - EAXSOURCE_MINOCCLUSION, - EAXSOURCE_MAXOCCLUSION); -} - -void ALsource::eax_validate_send_occlusion_lf_ratio( - float flOcclusionLFRatio) -{ - eax_validate_range<EaxSourceSendException>( - eax_get_occlusion_lf_ratio_name(), - flOcclusionLFRatio, - EAXSOURCE_MINOCCLUSIONLFRATIO, - EAXSOURCE_MAXOCCLUSIONLFRATIO); -} - -void ALsource::eax_validate_send_occlusion_room_ratio( - float flOcclusionRoomRatio) -{ - eax_validate_range<EaxSourceSendException>( - eax_get_occlusion_room_ratio_name(), - flOcclusionRoomRatio, - EAXSOURCE_MINOCCLUSIONROOMRATIO, - EAXSOURCE_MAXOCCLUSIONROOMRATIO); -} - -void ALsource::eax_validate_send_occlusion_direct_ratio( - float flOcclusionDirectRatio) -{ - eax_validate_range<EaxSourceSendException>( - eax_get_occlusion_direct_ratio_name(), - flOcclusionDirectRatio, - EAXSOURCE_MINOCCLUSIONDIRECTRATIO, - EAXSOURCE_MAXOCCLUSIONDIRECTRATIO); -} - -void ALsource::eax_validate_send_exclusion( - long lExclusion) -{ - eax_validate_range<EaxSourceSendException>( - eax_get_exclusion_name(), - lExclusion, - EAXSOURCE_MINEXCLUSION, - EAXSOURCE_MAXEXCLUSION); -} - -void ALsource::eax_validate_send_exclusion_lf_ratio( - float flExclusionLFRatio) -{ - eax_validate_range<EaxSourceSendException>( - eax_get_exclusion_lf_ratio_name(), - flExclusionLFRatio, - EAXSOURCE_MINEXCLUSIONLFRATIO, - EAXSOURCE_MAXEXCLUSIONLFRATIO); -} - -void ALsource::eax_validate_send( - const EAXSOURCESENDPROPERTIES& all) -{ - eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID); - eax_validate_send_send(all.lSend); - eax_validate_send_send_hf(all.lSendHF); -} - -void ALsource::eax_validate_send_exclusion_all( - const EAXSOURCEEXCLUSIONSENDPROPERTIES& all) -{ - eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID); - eax_validate_send_exclusion(all.lExclusion); - eax_validate_send_exclusion_lf_ratio(all.flExclusionLFRatio); -} - -void ALsource::eax_validate_send_occlusion_all( - const EAXSOURCEOCCLUSIONSENDPROPERTIES& all) +void ALsource::eax_set_defaults() noexcept { - eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID); - eax_validate_send_occlusion(all.lOcclusion); - eax_validate_send_occlusion_lf_ratio(all.flOcclusionLFRatio); - eax_validate_send_occlusion_room_ratio(all.flOcclusionRoomRatio); - eax_validate_send_occlusion_direct_ratio(all.flOcclusionDirectRatio); + eax1_set_defaults(); + eax2_set_defaults(); + eax3_set_defaults(); + eax4_set_defaults(); + eax5_set_defaults(); } -void ALsource::eax_validate_send_all( - const EAXSOURCEALLSENDPROPERTIES& all) +void ALsource::eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept { - eax_validate_send_receiving_fx_slot_guid(all.guidReceivingFXSlotID); - eax_validate_send_send(all.lSend); - eax_validate_send_send_hf(all.lSendHF); - eax_validate_send_occlusion(all.lOcclusion); - eax_validate_send_occlusion_lf_ratio(all.flOcclusionLFRatio); - eax_validate_send_occlusion_room_ratio(all.flOcclusionRoomRatio); - eax_validate_send_occlusion_direct_ratio(all.flOcclusionDirectRatio); - eax_validate_send_exclusion(all.lExclusion); - eax_validate_send_exclusion_lf_ratio(all.flExclusionLFRatio); -} + eax5_set_defaults(dst); -EaxFxSlotIndexValue ALsource::eax_get_send_index( - const GUID& send_guid) -{ - if (false) - { - } - else if (send_guid == EAXPROPERTYID_EAX40_FXSlot0 || send_guid == EAXPROPERTYID_EAX50_FXSlot0) - { - return 0; - } - else if (send_guid == EAXPROPERTYID_EAX40_FXSlot1 || send_guid == EAXPROPERTYID_EAX50_FXSlot1) - { - return 1; - } - else if (send_guid == EAXPROPERTYID_EAX40_FXSlot2 || send_guid == EAXPROPERTYID_EAX50_FXSlot2) - { - return 2; - } - else if (send_guid == EAXPROPERTYID_EAX40_FXSlot3 || send_guid == EAXPROPERTYID_EAX50_FXSlot3) - { - return 3; - } + if (src.fMix == EAX_REVERBMIX_USEDISTANCE) + dst.source.ulFlags |= EAXSOURCEFLAGS_ROOMAUTO; else - { - throw EaxSourceSendException{"Unsupported receiving FX slot GUID."}; - } -} - -void ALsource::eax_defer_send_send( - long lSend, - EaxFxSlotIndexValue index) -{ - eax_d_.sends[index].lSend = lSend; - - eax_sends_dirty_flags_.sends[index].lSend = - (eax_.sends[index].lSend != eax_d_.sends[index].lSend); -} - -void ALsource::eax_defer_send_send_hf( - long lSendHF, - EaxFxSlotIndexValue index) -{ - eax_d_.sends[index].lSendHF = lSendHF; - - eax_sends_dirty_flags_.sends[index].lSendHF = - (eax_.sends[index].lSendHF != eax_d_.sends[index].lSendHF); -} - -void ALsource::eax_defer_send_occlusion( - long lOcclusion, - EaxFxSlotIndexValue index) -{ - eax_d_.sends[index].lOcclusion = lOcclusion; - - eax_sends_dirty_flags_.sends[index].lOcclusion = - (eax_.sends[index].lOcclusion != eax_d_.sends[index].lOcclusion); -} - -void ALsource::eax_defer_send_occlusion_lf_ratio( - float flOcclusionLFRatio, - EaxFxSlotIndexValue index) -{ - eax_d_.sends[index].flOcclusionLFRatio = flOcclusionLFRatio; - - eax_sends_dirty_flags_.sends[index].flOcclusionLFRatio = - (eax_.sends[index].flOcclusionLFRatio != eax_d_.sends[index].flOcclusionLFRatio); -} - -void ALsource::eax_defer_send_occlusion_room_ratio( - float flOcclusionRoomRatio, - EaxFxSlotIndexValue index) -{ - eax_d_.sends[index].flOcclusionRoomRatio = flOcclusionRoomRatio; - - eax_sends_dirty_flags_.sends[index].flOcclusionRoomRatio = - (eax_.sends[index].flOcclusionRoomRatio != eax_d_.sends[index].flOcclusionRoomRatio); -} + dst.source.ulFlags &= ~EAXSOURCEFLAGS_ROOMAUTO; -void ALsource::eax_defer_send_occlusion_direct_ratio( - float flOcclusionDirectRatio, - EaxFxSlotIndexValue index) -{ - eax_d_.sends[index].flOcclusionDirectRatio = flOcclusionDirectRatio; - - eax_sends_dirty_flags_.sends[index].flOcclusionDirectRatio = - (eax_.sends[index].flOcclusionDirectRatio != eax_d_.sends[index].flOcclusionDirectRatio); -} - -void ALsource::eax_defer_send_exclusion( - long lExclusion, - EaxFxSlotIndexValue index) -{ - eax_d_.sends[index].lExclusion = lExclusion; - - eax_sends_dirty_flags_.sends[index].lExclusion = - (eax_.sends[index].lExclusion != eax_d_.sends[index].lExclusion); -} - -void ALsource::eax_defer_send_exclusion_lf_ratio( - float flExclusionLFRatio, - EaxFxSlotIndexValue index) -{ - eax_d_.sends[index].flExclusionLFRatio = flExclusionLFRatio; - - eax_sends_dirty_flags_.sends[index].flExclusionLFRatio = - (eax_.sends[index].flExclusionLFRatio != eax_d_.sends[index].flExclusionLFRatio); -} - -void ALsource::eax_defer_send( - const EAXSOURCESENDPROPERTIES& all, - EaxFxSlotIndexValue index) -{ - eax_defer_send_send(all.lSend, index); - eax_defer_send_send_hf(all.lSendHF, index); -} - -void ALsource::eax_defer_send_exclusion_all( - const EAXSOURCEEXCLUSIONSENDPROPERTIES& all, - EaxFxSlotIndexValue index) -{ - eax_defer_send_exclusion(all.lExclusion, index); - eax_defer_send_exclusion_lf_ratio(all.flExclusionLFRatio, index); -} - -void ALsource::eax_defer_send_occlusion_all( - const EAXSOURCEOCCLUSIONSENDPROPERTIES& all, - EaxFxSlotIndexValue index) -{ - eax_defer_send_occlusion(all.lOcclusion, index); - eax_defer_send_occlusion_lf_ratio(all.flOcclusionLFRatio, index); - eax_defer_send_occlusion_room_ratio(all.flOcclusionRoomRatio, index); - eax_defer_send_occlusion_direct_ratio(all.flOcclusionDirectRatio, index); -} - -void ALsource::eax_defer_send_all( - const EAXSOURCEALLSENDPROPERTIES& all, - EaxFxSlotIndexValue index) -{ - eax_defer_send_send(all.lSend, index); - eax_defer_send_send_hf(all.lSendHF, index); - eax_defer_send_occlusion(all.lOcclusion, index); - eax_defer_send_occlusion_lf_ratio(all.flOcclusionLFRatio, index); - eax_defer_send_occlusion_room_ratio(all.flOcclusionRoomRatio, index); - eax_defer_send_occlusion_direct_ratio(all.flOcclusionDirectRatio, index); - eax_defer_send_exclusion(all.lExclusion, index); - eax_defer_send_exclusion_lf_ratio(all.flExclusionLFRatio, index); + dst.sends[0].lSendHF = clamp( + static_cast<long>(gain_to_level_mb(src.fMix)), + EAXSOURCE_MINSENDHF, + EAXSOURCE_MAXSENDHF); } -void ALsource::eax_defer_send( - const EaxCall& call) -{ - const auto eax_all_span = - call.get_values<EaxSourceException, const EAXSOURCESENDPROPERTIES>(); - - const auto count = eax_all_span.size(); - - if (count <= 0 || count > EAX_MAX_FXSLOTS) - { - throw EaxSourceSendException{"Send count out of range."}; - } - - for (auto i = std::size_t{}; i < count; ++i) - { - const auto& all = eax_all_span[i]; - eax_validate_send(all); +void ALsource::eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept +{ + // Source. + // + dst.source.lDirect = src.lDirect; + dst.source.lDirectHF = src.lDirectHF; + dst.source.lRoom = src.lRoom; + dst.source.lRoomHF = src.lRoomHF; + dst.source.lObstruction = src.lObstruction; + dst.source.flObstructionLFRatio = src.flObstructionLFRatio; + dst.source.lOcclusion = src.lOcclusion; + dst.source.flOcclusionLFRatio = src.flOcclusionLFRatio; + dst.source.flOcclusionRoomRatio = src.flOcclusionRoomRatio; + dst.source.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO; + dst.source.lExclusion = EAXSOURCE_DEFAULTEXCLUSION; + dst.source.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO; + dst.source.lOutsideVolumeHF = src.lOutsideVolumeHF; + dst.source.flDopplerFactor = EAXSOURCE_DEFAULTDOPPLERFACTOR; + dst.source.flRolloffFactor = EAXSOURCE_DEFAULTROLLOFFFACTOR; + dst.source.flRoomRolloffFactor = src.flRoomRolloffFactor; + dst.source.flAirAbsorptionFactor = src.flAirAbsorptionFactor; + dst.source.ulFlags = src.dwFlags; + dst.source.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR; + + // Set everyting else to defaults. + // + eax5_set_sends_defaults(dst.sends); + eax5_set_active_fx_slots_defaults(dst.active_fx_slots); + eax5_set_speaker_levels_defaults(dst.speaker_levels); +} + +void ALsource::eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept +{ + // Source. + // + static_cast<Eax3Props&>(dst.source) = src; + dst.source.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR; + + // Set everyting else to defaults. + // + eax5_set_sends_defaults(dst.sends); + eax5_set_active_fx_slots_defaults(dst.active_fx_slots); + eax5_set_speaker_levels_defaults(dst.speaker_levels); +} + +void ALsource::eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept +{ + // Source. + // + static_cast<Eax3Props&>(dst.source) = src.source; + dst.source.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR; + + // Sends. + // + dst.sends = src.sends; + + for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) + dst.sends[i].guidReceivingFXSlotID = *(eax5_fx_slot_ids[i]); + + // Active FX slots. + // + for (auto i = 0; i < EAX50_MAX_ACTIVE_FXSLOTS; ++i) { + auto& dst_id = dst.active_fx_slots.guidActiveFXSlots[i]; + + if (i < EAX40_MAX_ACTIVE_FXSLOTS) { + const auto& src_id = src.active_fx_slots.guidActiveFXSlots[i]; + + if (src_id == EAX_NULL_GUID) + dst_id = EAX_NULL_GUID; + else if (src_id == EAX_PrimaryFXSlotID) + dst_id = EAX_PrimaryFXSlotID; + else if (src_id == EAXPROPERTYID_EAX40_FXSlot0) + dst_id = EAXPROPERTYID_EAX50_FXSlot0; + else if (src_id == EAXPROPERTYID_EAX40_FXSlot1) + dst_id = EAXPROPERTYID_EAX50_FXSlot1; + else if (src_id == EAXPROPERTYID_EAX40_FXSlot2) + dst_id = EAXPROPERTYID_EAX50_FXSlot2; + else if (src_id == EAXPROPERTYID_EAX40_FXSlot3) + dst_id = EAXPROPERTYID_EAX50_FXSlot3; + else + assert(false && "Unknown active FX slot ID."); + } else + dst_id = EAX_NULL_GUID; } - for (auto i = std::size_t{}; i < count; ++i) - { - const auto& all = eax_all_span[i]; - const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID); - eax_defer_send(all, send_index); - } + // Speaker levels. + // + eax5_set_speaker_levels_defaults(dst.speaker_levels); } -void ALsource::eax_defer_send_exclusion_all( - const EaxCall& call) +float ALsource::eax_calculate_dst_occlusion_mb( + long src_occlusion_mb, + float path_ratio, + float lf_ratio) noexcept { - const auto eax_all_span = - call.get_values<EaxSourceException, const EAXSOURCEEXCLUSIONSENDPROPERTIES>(); - - const auto count = eax_all_span.size(); - - if (count <= 0 || count > EAX_MAX_FXSLOTS) - { - throw EaxSourceSendException{"Send exclusion all count out of range."}; - } - - for (auto i = std::size_t{}; i < count; ++i) - { - const auto& all = eax_all_span[i]; - eax_validate_send_exclusion_all(all); - } - - for (auto i = std::size_t{}; i < count; ++i) - { - const auto& all = eax_all_span[i]; - const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID); - eax_defer_send_exclusion_all(all, send_index); - } + const auto ratio_1 = path_ratio + lf_ratio - 1.0F; + const auto ratio_2 = path_ratio * lf_ratio; + const auto ratio = (ratio_2 > ratio_1) ? ratio_2 : ratio_1; + const auto dst_occlustion_mb = static_cast<float>(src_occlusion_mb) * ratio; + return dst_occlustion_mb; } -void ALsource::eax_defer_send_occlusion_all( - const EaxCall& call) +EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept { - const auto eax_all_span = - call.get_values<EaxSourceException, const EAXSOURCEOCCLUSIONSENDPROPERTIES>(); - - const auto count = eax_all_span.size(); - - if (count <= 0 || count > EAX_MAX_FXSLOTS) - { - throw EaxSourceSendException{"Send occlusion all count out of range."}; - } + auto gain_mb = + static_cast<float>(eax_.source.lDirect) + + (static_cast<float>(eax_.source.lObstruction) * eax_.source.flObstructionLFRatio) + + eax_calculate_dst_occlusion_mb( + eax_.source.lOcclusion, + eax_.source.flOcclusionDirectRatio, + eax_.source.flOcclusionLFRatio); - for (auto i = std::size_t{}; i < count; ++i) - { - const auto& all = eax_all_span[i]; - eax_validate_send_occlusion_all(all); - } + auto gain_hf_mb = + static_cast<float>(eax_.source.lDirectHF) + + static_cast<float>(eax_.source.lObstruction) + + (static_cast<float>(eax_.source.lOcclusion) * eax_.source.flOcclusionDirectRatio); - for (auto i = std::size_t{}; i < count; ++i) + for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i) { - const auto& all = eax_all_span[i]; - const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID); - eax_defer_send_occlusion_all(all, send_index); - } -} - -void ALsource::eax_defer_send_all( - const EaxCall& call) -{ - const auto eax_all_span = - call.get_values<EaxSourceException, const EAXSOURCEALLSENDPROPERTIES>(); - - const auto count = eax_all_span.size(); + if (!eax_active_fx_slots_[i]) + { + continue; + } - if (count <= 0 || count > EAX_MAX_FXSLOTS) - { - throw EaxSourceSendException{"Send all count out of range."}; - } + const auto& send = eax_.sends[i]; - for (auto i = std::size_t{}; i < count; ++i) - { - const auto& all = eax_all_span[i]; - eax_validate_send_all(all); - } + gain_mb += eax_calculate_dst_occlusion_mb( + send.lOcclusion, + send.flOcclusionDirectRatio, + send.flOcclusionLFRatio); - for (auto i = std::size_t{}; i < count; ++i) - { - const auto& all = eax_all_span[i]; - const auto send_index = eax_get_send_index(all.guidReceivingFXSlotID); - eax_defer_send_all(all, send_index); + gain_hf_mb += static_cast<float>(send.lOcclusion) * send.flOcclusionDirectRatio; } -} + const auto al_low_pass_param = EaxAlLowPassParam{ + level_mb_to_gain(gain_mb), + minf(level_mb_to_gain(gain_hf_mb), 1.0f)}; -void ALsource::eax_validate_source_direct( - long direct) -{ - eax_validate_range<EaxSourceException>( - "Direct", - direct, - EAXSOURCE_MINDIRECT, - EAXSOURCE_MAXDIRECT); -} - -void ALsource::eax_validate_source_direct_hf( - long direct_hf) -{ - eax_validate_range<EaxSourceException>( - "Direct HF", - direct_hf, - EAXSOURCE_MINDIRECTHF, - EAXSOURCE_MAXDIRECTHF); -} - -void ALsource::eax_validate_source_room( - long room) -{ - eax_validate_range<EaxSourceException>( - "Room", - room, - EAXSOURCE_MINROOM, - EAXSOURCE_MAXROOM); -} - -void ALsource::eax_validate_source_room_hf( - long room_hf) -{ - eax_validate_range<EaxSourceException>( - "Room HF", - room_hf, - EAXSOURCE_MINROOMHF, - EAXSOURCE_MAXROOMHF); -} - -void ALsource::eax_validate_source_obstruction( - long obstruction) -{ - eax_validate_range<EaxSourceException>( - "Obstruction", - obstruction, - EAXSOURCE_MINOBSTRUCTION, - EAXSOURCE_MAXOBSTRUCTION); -} - -void ALsource::eax_validate_source_obstruction_lf_ratio( - float obstruction_lf_ratio) -{ - eax_validate_range<EaxSourceException>( - "Obstruction LF Ratio", - obstruction_lf_ratio, - EAXSOURCE_MINOBSTRUCTIONLFRATIO, - EAXSOURCE_MAXOBSTRUCTIONLFRATIO); -} - -void ALsource::eax_validate_source_occlusion( - long occlusion) -{ - eax_validate_range<EaxSourceException>( - eax_get_occlusion_name(), - occlusion, - EAXSOURCE_MINOCCLUSION, - EAXSOURCE_MAXOCCLUSION); -} - -void ALsource::eax_validate_source_occlusion_lf_ratio( - float occlusion_lf_ratio) -{ - eax_validate_range<EaxSourceException>( - eax_get_occlusion_lf_ratio_name(), - occlusion_lf_ratio, - EAXSOURCE_MINOCCLUSIONLFRATIO, - EAXSOURCE_MAXOCCLUSIONLFRATIO); -} - -void ALsource::eax_validate_source_occlusion_room_ratio( - float occlusion_room_ratio) -{ - eax_validate_range<EaxSourceException>( - eax_get_occlusion_room_ratio_name(), - occlusion_room_ratio, - EAXSOURCE_MINOCCLUSIONROOMRATIO, - EAXSOURCE_MAXOCCLUSIONROOMRATIO); -} - -void ALsource::eax_validate_source_occlusion_direct_ratio( - float occlusion_direct_ratio) -{ - eax_validate_range<EaxSourceException>( - eax_get_occlusion_direct_ratio_name(), - occlusion_direct_ratio, - EAXSOURCE_MINOCCLUSIONDIRECTRATIO, - EAXSOURCE_MAXOCCLUSIONDIRECTRATIO); -} - -void ALsource::eax_validate_source_exclusion( - long exclusion) -{ - eax_validate_range<EaxSourceException>( - eax_get_exclusion_name(), - exclusion, - EAXSOURCE_MINEXCLUSION, - EAXSOURCE_MAXEXCLUSION); -} - -void ALsource::eax_validate_source_exclusion_lf_ratio( - float exclusion_lf_ratio) -{ - eax_validate_range<EaxSourceException>( - eax_get_exclusion_lf_ratio_name(), - exclusion_lf_ratio, - EAXSOURCE_MINEXCLUSIONLFRATIO, - EAXSOURCE_MAXEXCLUSIONLFRATIO); -} - -void ALsource::eax_validate_source_outside_volume_hf( - long outside_volume_hf) -{ - eax_validate_range<EaxSourceException>( - "Outside Volume HF", - outside_volume_hf, - EAXSOURCE_MINOUTSIDEVOLUMEHF, - EAXSOURCE_MAXOUTSIDEVOLUMEHF); -} - -void ALsource::eax_validate_source_doppler_factor( - float doppler_factor) -{ - eax_validate_range<EaxSourceException>( - "Doppler Factor", - doppler_factor, - EAXSOURCE_MINDOPPLERFACTOR, - EAXSOURCE_MAXDOPPLERFACTOR); -} - -void ALsource::eax_validate_source_rolloff_factor( - float rolloff_factor) -{ - eax_validate_range<EaxSourceException>( - "Rolloff Factor", - rolloff_factor, - EAXSOURCE_MINROLLOFFFACTOR, - EAXSOURCE_MAXROLLOFFFACTOR); -} - -void ALsource::eax_validate_source_room_rolloff_factor( - float room_rolloff_factor) -{ - eax_validate_range<EaxSourceException>( - "Room Rolloff Factor", - room_rolloff_factor, - EAXSOURCE_MINROOMROLLOFFFACTOR, - EAXSOURCE_MAXROOMROLLOFFFACTOR); -} - -void ALsource::eax_validate_source_air_absorption_factor( - float air_absorption_factor) -{ - eax_validate_range<EaxSourceException>( - "Air Absorption Factor", - air_absorption_factor, - EAXSOURCE_MINAIRABSORPTIONFACTOR, - EAXSOURCE_MAXAIRABSORPTIONFACTOR); -} - -void ALsource::eax_validate_source_flags( - unsigned long flags, - int eax_version) -{ - eax_validate_range<EaxSourceException>( - "Flags", - flags, - 0UL, - ~((eax_version == 5) ? EAX50SOURCEFLAGS_RESERVED : EAX20SOURCEFLAGS_RESERVED)); -} - -void ALsource::eax_validate_source_macro_fx_factor( - float macro_fx_factor) -{ - eax_validate_range<EaxSourceException>( - "Macro FX Factor", - macro_fx_factor, - EAXSOURCE_MINMACROFXFACTOR, - EAXSOURCE_MAXMACROFXFACTOR); -} - -void ALsource::eax_validate_source_2d_all( - const EAXSOURCE2DPROPERTIES& all, - int eax_version) -{ - eax_validate_source_direct(all.lDirect); - eax_validate_source_direct_hf(all.lDirectHF); - eax_validate_source_room(all.lRoom); - eax_validate_source_room_hf(all.lRoomHF); - eax_validate_source_flags(all.ulFlags, eax_version); -} - -void ALsource::eax_validate_source_obstruction_all( - const EAXOBSTRUCTIONPROPERTIES& all) -{ - eax_validate_source_obstruction(all.lObstruction); - eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio); -} - -void ALsource::eax_validate_source_exclusion_all( - const EAXEXCLUSIONPROPERTIES& all) -{ - eax_validate_source_exclusion(all.lExclusion); - eax_validate_source_exclusion_lf_ratio(all.flExclusionLFRatio); -} - -void ALsource::eax_validate_source_occlusion_all( - const EAXOCCLUSIONPROPERTIES& all) -{ - eax_validate_source_occlusion(all.lOcclusion); - eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio); - eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio); - eax_validate_source_occlusion_direct_ratio(all.flOcclusionDirectRatio); -} - -void ALsource::eax_validate_source_all( - const EAX20BUFFERPROPERTIES& all, - int eax_version) -{ - eax_validate_source_direct(all.lDirect); - eax_validate_source_direct_hf(all.lDirectHF); - eax_validate_source_room(all.lRoom); - eax_validate_source_room_hf(all.lRoomHF); - eax_validate_source_obstruction(all.lObstruction); - eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio); - eax_validate_source_occlusion(all.lOcclusion); - eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio); - eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio); - eax_validate_source_outside_volume_hf(all.lOutsideVolumeHF); - eax_validate_source_room_rolloff_factor(all.flRoomRolloffFactor); - eax_validate_source_air_absorption_factor(all.flAirAbsorptionFactor); - eax_validate_source_flags(all.dwFlags, eax_version); -} - -void ALsource::eax_validate_source_all( - const EAX30SOURCEPROPERTIES& all, - int eax_version) -{ - eax_validate_source_direct(all.lDirect); - eax_validate_source_direct_hf(all.lDirectHF); - eax_validate_source_room(all.lRoom); - eax_validate_source_room_hf(all.lRoomHF); - eax_validate_source_obstruction(all.lObstruction); - eax_validate_source_obstruction_lf_ratio(all.flObstructionLFRatio); - eax_validate_source_occlusion(all.lOcclusion); - eax_validate_source_occlusion_lf_ratio(all.flOcclusionLFRatio); - eax_validate_source_occlusion_room_ratio(all.flOcclusionRoomRatio); - eax_validate_source_occlusion_direct_ratio(all.flOcclusionDirectRatio); - eax_validate_source_exclusion(all.lExclusion); - eax_validate_source_exclusion_lf_ratio(all.flExclusionLFRatio); - eax_validate_source_outside_volume_hf(all.lOutsideVolumeHF); - eax_validate_source_doppler_factor(all.flDopplerFactor); - eax_validate_source_rolloff_factor(all.flRolloffFactor); - eax_validate_source_room_rolloff_factor(all.flRoomRolloffFactor); - eax_validate_source_air_absorption_factor(all.flAirAbsorptionFactor); - eax_validate_source_flags(all.ulFlags, eax_version); -} - -void ALsource::eax_validate_source_all( - const EAX50SOURCEPROPERTIES& all, - int eax_version) -{ - eax_validate_source_all(static_cast<EAX30SOURCEPROPERTIES>(all), eax_version); - eax_validate_source_macro_fx_factor(all.flMacroFXFactor); -} - -void ALsource::eax_validate_source_speaker_id( - long speaker_id) -{ - eax_validate_range<EaxSourceException>( - "Speaker Id", - speaker_id, - static_cast<long>(EAXSPEAKER_FRONT_LEFT), - static_cast<long>(EAXSPEAKER_LOW_FREQUENCY)); -} - -void ALsource::eax_validate_source_speaker_level( - long speaker_level) -{ - eax_validate_range<EaxSourceException>( - "Speaker Level", - speaker_level, - EAXSOURCE_MINSPEAKERLEVEL, - EAXSOURCE_MAXSPEAKERLEVEL); -} - -void ALsource::eax_validate_source_speaker_level_all( - const EAXSPEAKERLEVELPROPERTIES& all) -{ - eax_validate_source_speaker_id(all.lSpeakerID); - eax_validate_source_speaker_level(all.lLevel); -} - -void ALsource::eax_defer_source_direct( - long lDirect) -{ - eax_d_.source.lDirect = lDirect; - eax_source_dirty_filter_flags_.lDirect = (eax_.source.lDirect != eax_d_.source.lDirect); -} - -void ALsource::eax_defer_source_direct_hf( - long lDirectHF) -{ - eax_d_.source.lDirectHF = lDirectHF; - eax_source_dirty_filter_flags_.lDirectHF = (eax_.source.lDirectHF != eax_d_.source.lDirectHF); -} - -void ALsource::eax_defer_source_room( - long lRoom) -{ - eax_d_.source.lRoom = lRoom; - eax_source_dirty_filter_flags_.lRoom = (eax_.source.lRoom != eax_d_.source.lRoom); -} - -void ALsource::eax_defer_source_room_hf( - long lRoomHF) -{ - eax_d_.source.lRoomHF = lRoomHF; - eax_source_dirty_filter_flags_.lRoomHF = (eax_.source.lRoomHF != eax_d_.source.lRoomHF); -} - -void ALsource::eax_defer_source_obstruction( - long lObstruction) -{ - eax_d_.source.lObstruction = lObstruction; - eax_source_dirty_filter_flags_.lObstruction = (eax_.source.lObstruction != eax_d_.source.lObstruction); -} - -void ALsource::eax_defer_source_obstruction_lf_ratio( - float flObstructionLFRatio) -{ - eax_d_.source.flObstructionLFRatio = flObstructionLFRatio; - eax_source_dirty_filter_flags_.flObstructionLFRatio = (eax_.source.flObstructionLFRatio != eax_d_.source.flObstructionLFRatio); -} - -void ALsource::eax_defer_source_occlusion( - long lOcclusion) -{ - eax_d_.source.lOcclusion = lOcclusion; - eax_source_dirty_filter_flags_.lOcclusion = (eax_.source.lOcclusion != eax_d_.source.lOcclusion); -} - -void ALsource::eax_defer_source_occlusion_lf_ratio( - float flOcclusionLFRatio) -{ - eax_d_.source.flOcclusionLFRatio = flOcclusionLFRatio; - eax_source_dirty_filter_flags_.flOcclusionLFRatio = (eax_.source.flOcclusionLFRatio != eax_d_.source.flOcclusionLFRatio); -} - -void ALsource::eax_defer_source_occlusion_room_ratio( - float flOcclusionRoomRatio) -{ - eax_d_.source.flOcclusionRoomRatio = flOcclusionRoomRatio; - eax_source_dirty_filter_flags_.flOcclusionRoomRatio = (eax_.source.flOcclusionRoomRatio != eax_d_.source.flOcclusionRoomRatio); -} - -void ALsource::eax_defer_source_occlusion_direct_ratio( - float flOcclusionDirectRatio) -{ - eax_d_.source.flOcclusionDirectRatio = flOcclusionDirectRatio; - eax_source_dirty_filter_flags_.flOcclusionDirectRatio = (eax_.source.flOcclusionDirectRatio != eax_d_.source.flOcclusionDirectRatio); -} - -void ALsource::eax_defer_source_exclusion( - long lExclusion) -{ - eax_d_.source.lExclusion = lExclusion; - eax_source_dirty_filter_flags_.lExclusion = (eax_.source.lExclusion != eax_d_.source.lExclusion); -} - -void ALsource::eax_defer_source_exclusion_lf_ratio( - float flExclusionLFRatio) -{ - eax_d_.source.flExclusionLFRatio = flExclusionLFRatio; - eax_source_dirty_filter_flags_.flExclusionLFRatio = (eax_.source.flExclusionLFRatio != eax_d_.source.flExclusionLFRatio); -} - -void ALsource::eax_defer_source_outside_volume_hf( - long lOutsideVolumeHF) -{ - eax_d_.source.lOutsideVolumeHF = lOutsideVolumeHF; - eax_source_dirty_misc_flags_.lOutsideVolumeHF = (eax_.source.lOutsideVolumeHF != eax_d_.source.lOutsideVolumeHF); -} - -void ALsource::eax_defer_source_doppler_factor( - float flDopplerFactor) -{ - eax_d_.source.flDopplerFactor = flDopplerFactor; - eax_source_dirty_misc_flags_.flDopplerFactor = (eax_.source.flDopplerFactor != eax_d_.source.flDopplerFactor); -} - -void ALsource::eax_defer_source_rolloff_factor( - float flRolloffFactor) -{ - eax_d_.source.flRolloffFactor = flRolloffFactor; - eax_source_dirty_misc_flags_.flRolloffFactor = (eax_.source.flRolloffFactor != eax_d_.source.flRolloffFactor); -} - -void ALsource::eax_defer_source_room_rolloff_factor( - float flRoomRolloffFactor) -{ - eax_d_.source.flRoomRolloffFactor = flRoomRolloffFactor; - eax_source_dirty_misc_flags_.flRoomRolloffFactor = (eax_.source.flRoomRolloffFactor != eax_d_.source.flRoomRolloffFactor); -} - -void ALsource::eax_defer_source_air_absorption_factor( - float flAirAbsorptionFactor) -{ - eax_d_.source.flAirAbsorptionFactor = flAirAbsorptionFactor; - eax_source_dirty_misc_flags_.flAirAbsorptionFactor = (eax_.source.flAirAbsorptionFactor != eax_d_.source.flAirAbsorptionFactor); -} - -void ALsource::eax_defer_source_flags( - unsigned long ulFlags) -{ - eax_d_.source.ulFlags = ulFlags; - eax_source_dirty_misc_flags_.ulFlags = (eax_.source.ulFlags != eax_d_.source.ulFlags); -} - -void ALsource::eax_defer_source_macro_fx_factor( - float flMacroFXFactor) -{ - eax_d_.source.flMacroFXFactor = flMacroFXFactor; - eax_source_dirty_misc_flags_.flMacroFXFactor = (eax_.source.flMacroFXFactor != eax_d_.source.flMacroFXFactor); -} - -void ALsource::eax_defer_source_2d_all( - const EAXSOURCE2DPROPERTIES& all) -{ - eax_defer_source_direct(all.lDirect); - eax_defer_source_direct_hf(all.lDirectHF); - eax_defer_source_room(all.lRoom); - eax_defer_source_room_hf(all.lRoomHF); - eax_defer_source_flags(all.ulFlags); -} - -void ALsource::eax_defer_source_obstruction_all( - const EAXOBSTRUCTIONPROPERTIES& all) -{ - eax_defer_source_obstruction(all.lObstruction); - eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio); -} - -void ALsource::eax_defer_source_exclusion_all( - const EAXEXCLUSIONPROPERTIES& all) -{ - eax_defer_source_exclusion(all.lExclusion); - eax_defer_source_exclusion_lf_ratio(all.flExclusionLFRatio); -} - -void ALsource::eax_defer_source_occlusion_all( - const EAXOCCLUSIONPROPERTIES& all) -{ - eax_defer_source_occlusion(all.lOcclusion); - eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio); - eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio); - eax_defer_source_occlusion_direct_ratio(all.flOcclusionDirectRatio); -} - -void ALsource::eax_defer_source_all( - const EAX20BUFFERPROPERTIES& all) -{ - eax_defer_source_direct(all.lDirect); - eax_defer_source_direct_hf(all.lDirectHF); - eax_defer_source_room(all.lRoom); - eax_defer_source_room_hf(all.lRoomHF); - eax_defer_source_obstruction(all.lObstruction); - eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio); - eax_defer_source_occlusion(all.lOcclusion); - eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio); - eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio); - eax_defer_source_outside_volume_hf(all.lOutsideVolumeHF); - eax_defer_source_room_rolloff_factor(all.flRoomRolloffFactor); - eax_defer_source_air_absorption_factor(all.flAirAbsorptionFactor); - eax_defer_source_flags(all.dwFlags); + return al_low_pass_param; } -void ALsource::eax_defer_source_all( - const EAX30SOURCEPROPERTIES& all) +EaxAlLowPassParam ALsource::eax_create_room_filter_param( + const ALeffectslot& fx_slot, + const EAXSOURCEALLSENDPROPERTIES& send) const noexcept { - eax_defer_source_direct(all.lDirect); - eax_defer_source_direct_hf(all.lDirectHF); - eax_defer_source_room(all.lRoom); - eax_defer_source_room_hf(all.lRoomHF); - eax_defer_source_obstruction(all.lObstruction); - eax_defer_source_obstruction_lf_ratio(all.flObstructionLFRatio); - eax_defer_source_occlusion(all.lOcclusion); - eax_defer_source_occlusion_lf_ratio(all.flOcclusionLFRatio); - eax_defer_source_occlusion_room_ratio(all.flOcclusionRoomRatio); - eax_defer_source_occlusion_direct_ratio(all.flOcclusionDirectRatio); - eax_defer_source_exclusion(all.lExclusion); - eax_defer_source_exclusion_lf_ratio(all.flExclusionLFRatio); - eax_defer_source_outside_volume_hf(all.lOutsideVolumeHF); - eax_defer_source_doppler_factor(all.flDopplerFactor); - eax_defer_source_rolloff_factor(all.flRolloffFactor); - eax_defer_source_room_rolloff_factor(all.flRoomRolloffFactor); - eax_defer_source_air_absorption_factor(all.flAirAbsorptionFactor); - eax_defer_source_flags(all.ulFlags); -} + const auto& fx_slot_eax = fx_slot.eax_get_eax_fx_slot(); -void ALsource::eax_defer_source_all( - const EAX50SOURCEPROPERTIES& all) -{ - eax_defer_source_all(static_cast<const EAX30SOURCEPROPERTIES&>(all)); - eax_defer_source_macro_fx_factor(all.flMacroFXFactor); -} + const auto gain_mb = + static_cast<float>(eax_.source.lRoom + send.lSend) + + eax_calculate_dst_occlusion_mb( + eax_.source.lOcclusion, + eax_.source.flOcclusionRoomRatio, + eax_.source.flOcclusionLFRatio) + + eax_calculate_dst_occlusion_mb( + send.lOcclusion, + send.flOcclusionRoomRatio, + send.flOcclusionLFRatio) + + (static_cast<float>(eax_.source.lExclusion) * eax_.source.flExclusionLFRatio) + + (static_cast<float>(send.lExclusion) * send.flExclusionLFRatio); -void ALsource::eax_defer_source_speaker_level_all( - const EAXSPEAKERLEVELPROPERTIES& all) -{ - const auto speaker_index = static_cast<std::size_t>(all.lSpeakerID - 1); - auto& speaker_level_d = eax_d_.speaker_levels[speaker_index]; - const auto& speaker_level = eax_.speaker_levels[speaker_index]; + const auto gain_hf_mb = + static_cast<float>(eax_.source.lRoomHF + send.lSendHF) + + (static_cast<float>(fx_slot_eax.lOcclusion + eax_.source.lOcclusion) * eax_.source.flOcclusionRoomRatio) + + (static_cast<float>(send.lOcclusion) * send.flOcclusionRoomRatio) + + static_cast<float>(eax_.source.lExclusion + send.lExclusion); - if (speaker_level != speaker_level_d) - { - eax_source_dirty_misc_flags_.speaker_levels = true; - } -} + const auto al_low_pass_param = EaxAlLowPassParam{ + level_mb_to_gain(gain_mb), + minf(level_mb_to_gain(gain_hf_mb), 1.0f)}; -ALuint ALsource::eax2_translate_property_id(const EaxCall& call) -{ - switch (call.get_property_id()) - { - case DSPROPERTY_EAX20BUFFER_NONE: return EAXSOURCE_NONE; - case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS: return EAXSOURCE_ALLPARAMETERS; - case DSPROPERTY_EAX20BUFFER_DIRECT: return EAXSOURCE_DIRECT; - case DSPROPERTY_EAX20BUFFER_DIRECTHF: return EAXSOURCE_DIRECTHF; - case DSPROPERTY_EAX20BUFFER_ROOM: return EAXSOURCE_ROOM; - case DSPROPERTY_EAX20BUFFER_ROOMHF: return EAXSOURCE_ROOMHF; - case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR: return EAXSOURCE_ROOMROLLOFFFACTOR; - case DSPROPERTY_EAX20BUFFER_OBSTRUCTION: return EAXSOURCE_OBSTRUCTION; - case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO: return EAXSOURCE_OBSTRUCTIONLFRATIO; - case DSPROPERTY_EAX20BUFFER_OCCLUSION: return EAXSOURCE_OCCLUSION; - case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO: return EAXSOURCE_OCCLUSIONLFRATIO; - case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO: return EAXSOURCE_OCCLUSIONROOMRATIO; - case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF: return EAXSOURCE_OUTSIDEVOLUMEHF; - case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR: return EAXSOURCE_AIRABSORPTIONFACTOR; - case DSPROPERTY_EAX20BUFFER_FLAGS: return EAXSOURCE_FLAGS; - default: eax_fail("Unknown property id."); - } + return al_low_pass_param; } -void ALsource::eax1_set_efx() +void ALsource::eax_update_direct_filter() { - const auto primary_fx_slot_index = eax_al_context_->eax_get_primary_fx_slot_index(); - - if (!primary_fx_slot_index.has_value()) - return; - - WetGainAuto = (eax1_.fMix == EAX_REVERBMIX_USEDISTANCE); - const auto filter_gain = (WetGainAuto ? 1.0F : eax1_.fMix); - auto& fx_slot = eax_al_context_->eax_get_fx_slot(*primary_fx_slot_index); - eax_set_al_source_send(&fx_slot, *primary_fx_slot_index, EaxAlLowPassParam{filter_gain, 1.0F}); + const auto& direct_param = eax_create_direct_filter_param(); + Direct.Gain = direct_param.gain; + Direct.GainHF = direct_param.gain_hf; + Direct.HFReference = LOWPASSFREQREF; + Direct.GainLF = 1.0f; + Direct.LFReference = HIGHPASSFREQREF; mPropsDirty = true; } -void ALsource::eax1_set_reverb_mix(const EaxCall& call) -{ - const auto reverb_mix = call.get_value<EaxSourceException, const decltype(EAXBUFFER_REVERBPROPERTIES::fMix)>(); - eax1_validate_reverb_mix(reverb_mix); - - if (eax1_.fMix == reverb_mix) - return; - - eax1_.fMix = reverb_mix; - eax1_set_efx(); -} - -void ALsource::eax_defer_source_direct( - const EaxCall& call) -{ - const auto direct = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lDirect)>(); - - eax_validate_source_direct(direct); - eax_defer_source_direct(direct); -} - -void ALsource::eax_defer_source_direct_hf( - const EaxCall& call) -{ - const auto direct_hf = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lDirectHF)>(); - - eax_validate_source_direct_hf(direct_hf); - eax_defer_source_direct_hf(direct_hf); -} - -void ALsource::eax_defer_source_room( - const EaxCall& call) -{ - const auto room = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lRoom)>(); - - eax_validate_source_room(room); - eax_defer_source_room(room); -} - -void ALsource::eax_defer_source_room_hf( - const EaxCall& call) -{ - const auto room_hf = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lRoomHF)>(); - - eax_validate_source_room_hf(room_hf); - eax_defer_source_room_hf(room_hf); -} - -void ALsource::eax_defer_source_obstruction( - const EaxCall& call) -{ - const auto obstruction = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lObstruction)>(); - - eax_validate_source_obstruction(obstruction); - eax_defer_source_obstruction(obstruction); -} - -void ALsource::eax_defer_source_obstruction_lf_ratio( - const EaxCall& call) -{ - const auto obstruction_lf_ratio = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flObstructionLFRatio)>(); - - eax_validate_source_obstruction_lf_ratio(obstruction_lf_ratio); - eax_defer_source_obstruction_lf_ratio(obstruction_lf_ratio); -} - -void ALsource::eax_defer_source_occlusion( - const EaxCall& call) -{ - const auto occlusion = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lOcclusion)>(); - - eax_validate_source_occlusion(occlusion); - eax_defer_source_occlusion(occlusion); -} - -void ALsource::eax_defer_source_occlusion_lf_ratio( - const EaxCall& call) -{ - const auto occlusion_lf_ratio = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flOcclusionLFRatio)>(); - - eax_validate_source_occlusion_lf_ratio(occlusion_lf_ratio); - eax_defer_source_occlusion_lf_ratio(occlusion_lf_ratio); -} - -void ALsource::eax_defer_source_occlusion_room_ratio( - const EaxCall& call) -{ - const auto occlusion_room_ratio = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flOcclusionRoomRatio)>(); - - eax_validate_source_occlusion_room_ratio(occlusion_room_ratio); - eax_defer_source_occlusion_room_ratio(occlusion_room_ratio); -} - -void ALsource::eax_defer_source_occlusion_direct_ratio( - const EaxCall& call) -{ - const auto occlusion_direct_ratio = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flOcclusionDirectRatio)>(); - - eax_validate_source_occlusion_direct_ratio(occlusion_direct_ratio); - eax_defer_source_occlusion_direct_ratio(occlusion_direct_ratio); -} - -void ALsource::eax_defer_source_exclusion( - const EaxCall& call) -{ - const auto exclusion = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lExclusion)>(); - - eax_validate_source_exclusion(exclusion); - eax_defer_source_exclusion(exclusion); -} - -void ALsource::eax_defer_source_exclusion_lf_ratio( - const EaxCall& call) -{ - const auto exclusion_lf_ratio = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flExclusionLFRatio)>(); - - eax_validate_source_exclusion_lf_ratio(exclusion_lf_ratio); - eax_defer_source_exclusion_lf_ratio(exclusion_lf_ratio); -} - -void ALsource::eax_defer_source_outside_volume_hf( - const EaxCall& call) -{ - const auto outside_volume_hf = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::lOutsideVolumeHF)>(); - - eax_validate_source_outside_volume_hf(outside_volume_hf); - eax_defer_source_outside_volume_hf(outside_volume_hf); -} - -void ALsource::eax_defer_source_doppler_factor( - const EaxCall& call) -{ - const auto doppler_factor = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flDopplerFactor)>(); - - eax_validate_source_doppler_factor(doppler_factor); - eax_defer_source_doppler_factor(doppler_factor); -} - -void ALsource::eax_defer_source_rolloff_factor( - const EaxCall& call) +void ALsource::eax_update_room_filters() { - const auto rolloff_factor = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flRolloffFactor)>(); - - eax_validate_source_rolloff_factor(rolloff_factor); - eax_defer_source_rolloff_factor(rolloff_factor); -} - -void ALsource::eax_defer_source_room_rolloff_factor( - const EaxCall& call) -{ - const auto room_rolloff_factor = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flRoomRolloffFactor)>(); - - eax_validate_source_room_rolloff_factor(room_rolloff_factor); - eax_defer_source_room_rolloff_factor(room_rolloff_factor); -} - -void ALsource::eax_defer_source_air_absorption_factor( - const EaxCall& call) -{ - const auto air_absorption_factor = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::flAirAbsorptionFactor)>(); - - eax_validate_source_air_absorption_factor(air_absorption_factor); - eax_defer_source_air_absorption_factor(air_absorption_factor); -} - -void ALsource::eax_defer_source_flags( - const EaxCall& call) -{ - const auto flags = - call.get_value<EaxSourceException, const decltype(EAX30SOURCEPROPERTIES::ulFlags)>(); - - eax_validate_source_flags(flags, call.get_version()); - eax_defer_source_flags(flags); -} - -void ALsource::eax_defer_source_macro_fx_factor( - const EaxCall& call) -{ - const auto macro_fx_factor = - call.get_value<EaxSourceException, const decltype(EAX50SOURCEPROPERTIES::flMacroFXFactor)>(); - - eax_validate_source_macro_fx_factor(macro_fx_factor); - eax_defer_source_macro_fx_factor(macro_fx_factor); -} - -void ALsource::eax_defer_source_2d_all( - const EaxCall& call) -{ - const auto all = call.get_value<EaxSourceException, const EAXSOURCE2DPROPERTIES>(); - - eax_validate_source_2d_all(all, call.get_version()); - eax_defer_source_2d_all(all); -} - -void ALsource::eax_defer_source_obstruction_all( - const EaxCall& call) -{ - const auto all = call.get_value<EaxSourceException, const EAXOBSTRUCTIONPROPERTIES>(); - - eax_validate_source_obstruction_all(all); - eax_defer_source_obstruction_all(all); -} - -void ALsource::eax_defer_source_exclusion_all( - const EaxCall& call) -{ - const auto all = call.get_value<EaxSourceException, const EAXEXCLUSIONPROPERTIES>(); - - eax_validate_source_exclusion_all(all); - eax_defer_source_exclusion_all(all); -} - -void ALsource::eax_defer_source_occlusion_all( - const EaxCall& call) -{ - const auto all = call.get_value<EaxSourceException, const EAXOCCLUSIONPROPERTIES>(); - - eax_validate_source_occlusion_all(all); - eax_defer_source_occlusion_all(all); -} - -void ALsource::eax_defer_source_all( - const EaxCall& call) -{ - const auto eax_version = call.get_version(); - - if (eax_version == 2) - { - const auto all = call.get_value<EaxSourceException, const EAX20BUFFERPROPERTIES>(); - - eax_validate_source_all(all, eax_version); - eax_defer_source_all(all); - } - else if (eax_version < 5) - { - const auto all = call.get_value<EaxSourceException, const EAX30SOURCEPROPERTIES>(); - - eax_validate_source_all(all, eax_version); - eax_defer_source_all(all); - } - else - { - const auto all = call.get_value<EaxSourceException, const EAX50SOURCEPROPERTIES>(); + for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) { + if (!eax_active_fx_slots_[i]) + continue; - eax_validate_source_all(all, eax_version); - eax_defer_source_all(all); + auto& fx_slot = eax_al_context_->eax_get_fx_slot(i); + const auto& send = eax_.sends[i]; + const auto& room_param = eax_create_room_filter_param(fx_slot, send); + eax_set_al_source_send(&fx_slot, i, room_param); } } -void ALsource::eax_defer_source_speaker_level_all( - const EaxCall& call) -{ - const auto speaker_level_properties = call.get_value<EaxSourceException, const EAXSPEAKERLEVELPROPERTIES>(); - - eax_validate_source_speaker_level_all(speaker_level_properties); - eax_defer_source_speaker_level_all(speaker_level_properties); -} - -void ALsource::eax_set_outside_volume_hf() +void ALsource::eax_set_efx_outer_gain_hf() { - const auto efx_gain_hf = clamp( + OuterGainHF = clamp( level_mb_to_gain(static_cast<float>(eax_.source.lOutsideVolumeHF)), AL_MIN_CONE_OUTER_GAINHF, - AL_MAX_CONE_OUTER_GAINHF - ); - - OuterGainHF = efx_gain_hf; + AL_MAX_CONE_OUTER_GAINHF); } -void ALsource::eax_set_doppler_factor() +void ALsource::eax_set_efx_doppler_factor() { DopplerFactor = eax_.source.flDopplerFactor; } -void ALsource::eax_set_rolloff_factor() +void ALsource::eax_set_efx_rolloff_factor() { RolloffFactor2 = eax_.source.flRolloffFactor; } -void ALsource::eax_set_room_rolloff_factor() +void ALsource::eax_set_efx_room_rolloff_factor() { RoomRolloffFactor = eax_.source.flRoomRolloffFactor; } -void ALsource::eax_set_air_absorption_factor() +void ALsource::eax_set_efx_air_absorption_factor() { AirAbsorptionFactor = eax_.source.flAirAbsorptionFactor; } -void ALsource::eax_set_direct_hf_auto_flag() -{ - const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_DIRECTHFAUTO) != 0; - - DryGainHFAuto = is_enable; -} - -void ALsource::eax_set_room_auto_flag() -{ - const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMAUTO) != 0; - - WetGainAuto = is_enable; -} - -void ALsource::eax_set_room_hf_auto_flag() -{ - const auto is_enable = (eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMHFAUTO) != 0; - - WetGainHFAuto = is_enable; -} - -void ALsource::eax_set_flags() +void ALsource::eax_set_efx_dry_gain_hf_auto() { - eax_set_direct_hf_auto_flag(); - eax_set_room_auto_flag(); - eax_set_room_hf_auto_flag(); - eax_set_speaker_levels(); + DryGainHFAuto = ((eax_.source.ulFlags & EAXSOURCEFLAGS_DIRECTHFAUTO) != 0); } -void ALsource::eax_set_macro_fx_factor() +void ALsource::eax_set_efx_wet_gain_auto() { - // TODO + WetGainAuto = ((eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMAUTO) != 0); } -void ALsource::eax_set_speaker_levels() +void ALsource::eax_set_efx_wet_gain_hf_auto() { - // TODO + WetGainHFAuto = ((eax_.source.ulFlags & EAXSOURCEFLAGS_ROOMHFAUTO) != 0); } -void ALsource::eax1_set(const EaxCall& call) +void ALsource::eax1_set(const EaxCall& call, Eax1Props& props) { - switch (call.get_property_id()) - { + switch (call.get_property_id()) { case DSPROPERTY_EAXBUFFER_ALL: + eax_defer<Eax1SourceAllValidator>(call, props); + break; + case DSPROPERTY_EAXBUFFER_REVERBMIX: - eax1_set_reverb_mix(call); + eax_defer<Eax1SourceReverbMixValidator>(call, props.fMix); break; default: - eax_fail("Unsupported property id."); + eax_fail_unknown_property_id(); } } -void ALsource::eax_apply_deferred() +void ALsource::eax2_set(const EaxCall& call, Eax2Props& props) { - if (!eax_are_active_fx_slots_dirty_ && - eax_sends_dirty_flags_ == EaxSourceSendsDirtyFlags{} && - eax_source_dirty_filter_flags_ == EaxSourceSourceFilterDirtyFlags{} && - eax_source_dirty_misc_flags_ == EaxSourceSourceMiscDirtyFlags{}) - { - return; - } + switch (call.get_property_id()) { + case DSPROPERTY_EAX20BUFFER_NONE: + break; - eax_ = eax_d_; + case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS: + eax_defer<Eax2SourceAllValidator>(call, props); + break; - if (eax_are_active_fx_slots_dirty_) - { - eax_are_active_fx_slots_dirty_ = false; - eax_set_fx_slots(); - eax_update_filters_internal(); - } - else if (eax_has_active_fx_slots_) - { - if (eax_source_dirty_filter_flags_ != EaxSourceSourceFilterDirtyFlags{}) - { - eax_update_filters_internal(); - } - else if (eax_sends_dirty_flags_ != EaxSourceSendsDirtyFlags{}) - { - for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i) - { - if (eax_active_fx_slots_[i]) - { - if (eax_sends_dirty_flags_.sends[i] != EaxSourceSendDirtyFlags{}) - { - eax_update_filters_internal(); - break; - } - } - } - } - } + case DSPROPERTY_EAX20BUFFER_DIRECT: + eax_defer<Eax2SourceDirectValidator>(call, props.lDirect); + break; - if (eax_source_dirty_misc_flags_ != EaxSourceSourceMiscDirtyFlags{}) - { - if (eax_source_dirty_misc_flags_.lOutsideVolumeHF) - { - eax_set_outside_volume_hf(); - } + case DSPROPERTY_EAX20BUFFER_DIRECTHF: + eax_defer<Eax2SourceDirectHfValidator>(call, props.lDirectHF); + break; - if (eax_source_dirty_misc_flags_.flDopplerFactor) - { - eax_set_doppler_factor(); - } + case DSPROPERTY_EAX20BUFFER_ROOM: + eax_defer<Eax2SourceRoomValidator>(call, props.lRoom); + break; - if (eax_source_dirty_misc_flags_.flRolloffFactor) - { - eax_set_rolloff_factor(); - } + case DSPROPERTY_EAX20BUFFER_ROOMHF: + eax_defer<Eax2SourceRoomHfValidator>(call, props.lRoomHF); + break; - if (eax_source_dirty_misc_flags_.flRoomRolloffFactor) - { - eax_set_room_rolloff_factor(); - } + case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR: + eax_defer<Eax2SourceRoomRolloffFactorValidator>(call, props.flRoomRolloffFactor); + break; - if (eax_source_dirty_misc_flags_.flAirAbsorptionFactor) - { - eax_set_air_absorption_factor(); - } + case DSPROPERTY_EAX20BUFFER_OBSTRUCTION: + eax_defer<Eax2SourceObstructionValidator>(call, props.lObstruction); + break; - if (eax_source_dirty_misc_flags_.ulFlags) - { - eax_set_flags(); - } + case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO: + eax_defer<Eax2SourceObstructionLfRatioValidator>(call, props.flObstructionLFRatio); + break; - if (eax_source_dirty_misc_flags_.flMacroFXFactor) - { - eax_set_macro_fx_factor(); - } + case DSPROPERTY_EAX20BUFFER_OCCLUSION: + eax_defer<Eax2SourceOcclusionValidator>(call, props.lOcclusion); + break; - mPropsDirty = true; + case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO: + eax_defer<Eax2SourceOcclusionLfRatioValidator>(call, props.flOcclusionLFRatio); + break; - eax_source_dirty_misc_flags_ = EaxSourceSourceMiscDirtyFlags{}; - } + case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO: + eax_defer<Eax2SourceOcclusionRoomRatioValidator>(call, props.flOcclusionRoomRatio); + break; - eax_sends_dirty_flags_ = EaxSourceSendsDirtyFlags{}; - eax_source_dirty_filter_flags_ = EaxSourceSourceFilterDirtyFlags{}; -} + case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF: + eax_defer<Eax2SourceOutsideVolumeHfValidator>(call, props.lOutsideVolumeHF); + break; -void ALsource::eax_set( - const EaxCall& call) -{ - const auto version = call.get_version(); + case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR: + eax_defer<Eax2SourceAirAbsorptionFactorValidator>(call, props.flAirAbsorptionFactor); + break; - if (version == 1) - { - eax1_set(call); - return; - } + case DSPROPERTY_EAX20BUFFER_FLAGS: + eax_defer<Eax2SourceFlagsValidator>(call, props.dwFlags); + break; - const auto property_id = (version == 2 ? eax2_translate_property_id(call) : call.get_property_id()); + default: + eax_fail_unknown_property_id(); + } +} - switch (property_id) - { +void ALsource::eax3_set(const EaxCall& call, Eax3Props& props) +{ + switch (call.get_property_id()) { case EAXSOURCE_NONE: break; case EAXSOURCE_ALLPARAMETERS: - eax_defer_source_all(call); + eax_defer<Eax3SourceAllValidator>(call, props); break; case EAXSOURCE_OBSTRUCTIONPARAMETERS: - eax_defer_source_obstruction_all(call); + eax_defer_sub<Eax4ObstructionValidator, EAXOBSTRUCTIONPROPERTIES>(call, props.lObstruction); break; case EAXSOURCE_OCCLUSIONPARAMETERS: - eax_defer_source_occlusion_all(call); + eax_defer_sub<Eax4OcclusionValidator, EAXOCCLUSIONPROPERTIES>(call, props.lOcclusion); break; case EAXSOURCE_EXCLUSIONPARAMETERS: - eax_defer_source_exclusion_all(call); + eax_defer_sub<Eax4ExclusionValidator, EAXEXCLUSIONPROPERTIES>(call, props.lExclusion); break; case EAXSOURCE_DIRECT: - eax_defer_source_direct(call); + eax_defer<Eax2SourceDirectValidator>(call, props.lDirect); break; case EAXSOURCE_DIRECTHF: - eax_defer_source_direct_hf(call); + eax_defer<Eax2SourceDirectHfValidator>(call, props.lDirectHF); break; case EAXSOURCE_ROOM: - eax_defer_source_room(call); + eax_defer<Eax2SourceRoomValidator>(call, props.lRoom); break; case EAXSOURCE_ROOMHF: - eax_defer_source_room_hf(call); + eax_defer<Eax2SourceRoomHfValidator>(call, props.lRoomHF); break; case EAXSOURCE_OBSTRUCTION: - eax_defer_source_obstruction(call); + eax_defer<Eax2SourceObstructionValidator>(call, props.lObstruction); break; case EAXSOURCE_OBSTRUCTIONLFRATIO: - eax_defer_source_obstruction_lf_ratio(call); + eax_defer<Eax2SourceObstructionLfRatioValidator>(call, props.flObstructionLFRatio); break; case EAXSOURCE_OCCLUSION: - eax_defer_source_occlusion(call); + eax_defer<Eax2SourceOcclusionValidator>(call, props.lOcclusion); break; case EAXSOURCE_OCCLUSIONLFRATIO: - eax_defer_source_occlusion_lf_ratio(call); + eax_defer<Eax2SourceOcclusionLfRatioValidator>(call, props.flOcclusionLFRatio); break; case EAXSOURCE_OCCLUSIONROOMRATIO: - eax_defer_source_occlusion_room_ratio(call); + eax_defer<Eax2SourceOcclusionRoomRatioValidator>(call, props.flOcclusionRoomRatio); break; case EAXSOURCE_OCCLUSIONDIRECTRATIO: - eax_defer_source_occlusion_direct_ratio(call); + eax_defer<Eax3SourceOcclusionDirectRatioValidator>(call, props.flOcclusionDirectRatio); break; case EAXSOURCE_EXCLUSION: - eax_defer_source_exclusion(call); + eax_defer<Eax3SourceExclusionValidator>(call, props.lExclusion); break; case EAXSOURCE_EXCLUSIONLFRATIO: - eax_defer_source_exclusion_lf_ratio(call); + eax_defer<Eax3SourceExclusionLfRatioValidator>(call, props.flExclusionLFRatio); break; case EAXSOURCE_OUTSIDEVOLUMEHF: - eax_defer_source_outside_volume_hf(call); + eax_defer<Eax2SourceOutsideVolumeHfValidator>(call, props.lOutsideVolumeHF); break; case EAXSOURCE_DOPPLERFACTOR: - eax_defer_source_doppler_factor(call); + eax_defer<Eax3SourceDopplerFactorValidator>(call, props.flDopplerFactor); break; case EAXSOURCE_ROLLOFFFACTOR: - eax_defer_source_rolloff_factor(call); + eax_defer<Eax3SourceRolloffFactorValidator>(call, props.flRolloffFactor); break; case EAXSOURCE_ROOMROLLOFFFACTOR: - eax_defer_source_room_rolloff_factor(call); + eax_defer<Eax2SourceRoomRolloffFactorValidator>(call, props.flRoomRolloffFactor); break; case EAXSOURCE_AIRABSORPTIONFACTOR: - eax_defer_source_air_absorption_factor(call); + eax_defer<Eax2SourceAirAbsorptionFactorValidator>(call, props.flAirAbsorptionFactor); + break; + + case EAXSOURCE_FLAGS: + eax_defer<Eax2SourceFlagsValidator>(call, props.ulFlags); break; + default: + eax_fail_unknown_property_id(); + } +} + +void ALsource::eax4_set(const EaxCall& call, Eax4Props& props) +{ + switch (call.get_property_id()) { + case EAXSOURCE_NONE: + case EAXSOURCE_ALLPARAMETERS: + case EAXSOURCE_OBSTRUCTIONPARAMETERS: + case EAXSOURCE_OCCLUSIONPARAMETERS: + case EAXSOURCE_EXCLUSIONPARAMETERS: + case EAXSOURCE_DIRECT: + case EAXSOURCE_DIRECTHF: + case EAXSOURCE_ROOM: + case EAXSOURCE_ROOMHF: + case EAXSOURCE_OBSTRUCTION: + case EAXSOURCE_OBSTRUCTIONLFRATIO: + case EAXSOURCE_OCCLUSION: + case EAXSOURCE_OCCLUSIONLFRATIO: + case EAXSOURCE_OCCLUSIONROOMRATIO: + case EAXSOURCE_OCCLUSIONDIRECTRATIO: + case EAXSOURCE_EXCLUSION: + case EAXSOURCE_EXCLUSIONLFRATIO: + case EAXSOURCE_OUTSIDEVOLUMEHF: + case EAXSOURCE_DOPPLERFACTOR: + case EAXSOURCE_ROLLOFFFACTOR: + case EAXSOURCE_ROOMROLLOFFFACTOR: + case EAXSOURCE_AIRABSORPTIONFACTOR: case EAXSOURCE_FLAGS: - eax_defer_source_flags(call); + eax3_set(call, props.source); break; case EAXSOURCE_SENDPARAMETERS: - eax_defer_send(call); + eax4_defer_sends<Eax4SendValidator, EAXSOURCESENDPROPERTIES>(call, props.sends); break; case EAXSOURCE_ALLSENDPARAMETERS: - eax_defer_send_all(call); + eax4_defer_sends<Eax4AllSendValidator, EAXSOURCEALLSENDPROPERTIES>(call, props.sends); break; case EAXSOURCE_OCCLUSIONSENDPARAMETERS: - eax_defer_send_occlusion_all(call); + eax4_defer_sends<Eax4OcclusionSendValidator, EAXSOURCEOCCLUSIONSENDPROPERTIES>(call, props.sends); break; case EAXSOURCE_EXCLUSIONSENDPARAMETERS: - eax_defer_send_exclusion_all(call); + eax4_defer_sends<Eax4ExclusionSendValidator, EAXSOURCEEXCLUSIONSENDPROPERTIES>(call, props.sends); break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_defer_active_fx_slots(call); + eax4_defer_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); break; - case EAXSOURCE_MACROFXFACTOR: - eax_defer_source_macro_fx_factor(call); - break; + default: + eax_fail_unknown_property_id(); + } +} - case EAXSOURCE_SPEAKERLEVELS: - eax_defer_source_speaker_level_all(call); - break; +void ALsource::eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props) +{ + const auto& src_props = call.get_value<Exception, const EAXSOURCE2DPROPERTIES>(); + Eax5SourceAll2dValidator{}(src_props); + props.lDirect = src_props.lDirect; + props.lDirectHF = src_props.lDirectHF; + props.lRoom = src_props.lRoom; + props.lRoomHF = src_props.lRoomHF; + props.ulFlags = src_props.ulFlags; +} - case EAXSOURCE_ALL2DPARAMETERS: - eax_defer_source_2d_all(call); - break; +void ALsource::eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props) +{ + const auto values = call.get_values<const EAXSPEAKERLEVELPROPERTIES>(eax_max_speakers); + std::for_each(values.cbegin(), values.cend(), Eax5SpeakerAllValidator{}); - default: - eax_fail("Unsupported property id."); + for (const auto& value : values) { + const auto index = static_cast<size_t>(value.lSpeakerID - EAXSPEAKER_FRONT_LEFT); + props[index].lLevel = value.lLevel; } } -const GUID& ALsource::eax_get_send_fx_slot_guid( - int eax_version, - EaxFxSlotIndexValue fx_slot_index) +void ALsource::eax5_set(const EaxCall& call, Eax5Props& props) { - switch (eax_version) - { - case 4: - switch (fx_slot_index) - { - case 0: - return EAXPROPERTYID_EAX40_FXSlot0; + switch (call.get_property_id()) { + case EAXSOURCE_NONE: + break; - case 1: - return EAXPROPERTYID_EAX40_FXSlot1; + case EAXSOURCE_ALLPARAMETERS: + eax_defer<Eax5SourceAllValidator>(call, props.source); + break; - case 2: - return EAXPROPERTYID_EAX40_FXSlot2; + case EAXSOURCE_OBSTRUCTIONPARAMETERS: + case EAXSOURCE_OCCLUSIONPARAMETERS: + case EAXSOURCE_EXCLUSIONPARAMETERS: + case EAXSOURCE_DIRECT: + case EAXSOURCE_DIRECTHF: + case EAXSOURCE_ROOM: + case EAXSOURCE_ROOMHF: + case EAXSOURCE_OBSTRUCTION: + case EAXSOURCE_OBSTRUCTIONLFRATIO: + case EAXSOURCE_OCCLUSION: + case EAXSOURCE_OCCLUSIONLFRATIO: + case EAXSOURCE_OCCLUSIONROOMRATIO: + case EAXSOURCE_OCCLUSIONDIRECTRATIO: + case EAXSOURCE_EXCLUSION: + case EAXSOURCE_EXCLUSIONLFRATIO: + case EAXSOURCE_OUTSIDEVOLUMEHF: + case EAXSOURCE_DOPPLERFACTOR: + case EAXSOURCE_ROLLOFFFACTOR: + case EAXSOURCE_ROOMROLLOFFFACTOR: + case EAXSOURCE_AIRABSORPTIONFACTOR: + case EAXSOURCE_FLAGS: + eax3_set(call, props.source); + break; - case 3: - return EAXPROPERTYID_EAX40_FXSlot3; + case EAXSOURCE_SENDPARAMETERS: + eax5_defer_sends<Eax5SendValidator, EAXSOURCESENDPROPERTIES>(call, props.sends); + break; - default: - eax_fail("FX slot index out of range."); - } + case EAXSOURCE_ALLSENDPARAMETERS: + eax5_defer_sends<Eax5AllSendValidator, EAXSOURCEALLSENDPROPERTIES>(call, props.sends); + break; - case 5: - switch (fx_slot_index) - { - case 0: - return EAXPROPERTYID_EAX50_FXSlot0; + case EAXSOURCE_OCCLUSIONSENDPARAMETERS: + eax5_defer_sends<Eax5OcclusionSendValidator, EAXSOURCEOCCLUSIONSENDPROPERTIES>(call, props.sends); + break; - case 1: - return EAXPROPERTYID_EAX50_FXSlot1; + case EAXSOURCE_EXCLUSIONSENDPARAMETERS: + eax5_defer_sends<Eax5ExclusionSendValidator, EAXSOURCEEXCLUSIONSENDPROPERTIES>(call, props.sends); + break; - case 2: - return EAXPROPERTYID_EAX50_FXSlot2; + case EAXSOURCE_ACTIVEFXSLOTID: + eax5_defer_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); + break; - case 3: - return EAXPROPERTYID_EAX50_FXSlot3; + case EAXSOURCE_MACROFXFACTOR: + eax_defer<Eax5SourceMacroFXFactorValidator>(call, props.source.flMacroFXFactor); + break; - default: - eax_fail("FX slot index out of range."); - } + case EAXSOURCE_SPEAKERLEVELS: + eax5_defer_speaker_levels(call, props.speaker_levels); + break; + + case EAXSOURCE_ALL2DPARAMETERS: + eax5_defer_all_2d(call, props.source); + break; default: - eax_fail("Unsupported EAX version."); + eax_fail_unknown_property_id(); } } -void ALsource::eax_copy_send( - const EAXSOURCEALLSENDPROPERTIES& src_send, - EAXSOURCESENDPROPERTIES& dst_send) -{ - dst_send.lSend = src_send.lSend; - dst_send.lSendHF = src_send.lSendHF; -} - -void ALsource::eax_copy_send( - const EAXSOURCEALLSENDPROPERTIES& src_send, - EAXSOURCEALLSENDPROPERTIES& dst_send) +void ALsource::eax_set(const EaxCall& call) { - dst_send = src_send; -} - -void ALsource::eax_copy_send( - const EAXSOURCEALLSENDPROPERTIES& src_send, - EAXSOURCEOCCLUSIONSENDPROPERTIES& dst_send) -{ - dst_send.lOcclusion = src_send.lOcclusion; - dst_send.flOcclusionLFRatio = src_send.flOcclusionLFRatio; - dst_send.flOcclusionRoomRatio = src_send.flOcclusionRoomRatio; - dst_send.flOcclusionDirectRatio = src_send.flOcclusionDirectRatio; -} - -void ALsource::eax_copy_send( - const EAXSOURCEALLSENDPROPERTIES& src_send, - EAXSOURCEEXCLUSIONSENDPROPERTIES& dst_send) -{ - dst_send.lExclusion = src_send.lExclusion; - dst_send.flExclusionLFRatio = src_send.flExclusionLFRatio; + switch (call.get_version()) { + case 1: eax1_set(call, eax1_.d); break; + case 2: eax2_set(call, eax2_.d); break; + case 3: eax3_set(call, eax3_.d); break; + case 4: eax4_set(call, eax4_.d); break; + case 5: eax5_set(call, eax5_.d); break; + default: eax_fail_unknown_property_id(); + } } -void ALsource::eax1_get(const EaxCall& call) +void ALsource::eax1_get(const EaxCall& call, const Eax1Props& props) { - switch (call.get_property_id()) - { + switch (call.get_property_id()) { case DSPROPERTY_EAXBUFFER_ALL: case DSPROPERTY_EAXBUFFER_REVERBMIX: - call.set_value<EaxSourceException>(eax1_); + call.set_value<Exception>(props.fMix); break; default: - eax_fail("Unsupported property id."); + eax_fail_unknown_property_id(); } } -void ALsource::eax_api_get_source_all_v2( - const EaxCall& call) +void ALsource::eax2_get(const EaxCall& call, const Eax2Props& props) { - auto eax_2_all = EAX20BUFFERPROPERTIES{}; - eax_2_all.lDirect = eax_.source.lDirect; - eax_2_all.lDirectHF = eax_.source.lDirectHF; - eax_2_all.lRoom = eax_.source.lRoom; - eax_2_all.lRoomHF = eax_.source.lRoomHF; - eax_2_all.flRoomRolloffFactor = eax_.source.flRoomRolloffFactor; - eax_2_all.lObstruction = eax_.source.lObstruction; - eax_2_all.flObstructionLFRatio = eax_.source.flObstructionLFRatio; - eax_2_all.lOcclusion = eax_.source.lOcclusion; - eax_2_all.flOcclusionLFRatio = eax_.source.flOcclusionLFRatio; - eax_2_all.flOcclusionRoomRatio = eax_.source.flOcclusionRoomRatio; - eax_2_all.lOutsideVolumeHF = eax_.source.lOutsideVolumeHF; - eax_2_all.flAirAbsorptionFactor = eax_.source.flAirAbsorptionFactor; - eax_2_all.dwFlags = eax_.source.ulFlags; - - call.set_value<EaxSourceException>(eax_2_all); -} + switch (call.get_property_id()) { + case DSPROPERTY_EAX20BUFFER_NONE: + break; -void ALsource::eax_api_get_source_all_v3( - const EaxCall& call) -{ - call.set_value<EaxSourceException>(static_cast<const EAX30SOURCEPROPERTIES&>(eax_.source)); -} + case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS: + call.set_value<Exception>(props); + break; -void ALsource::eax_api_get_source_all_v5( - const EaxCall& call) -{ - call.set_value<EaxSourceException>(eax_.source); -} + case DSPROPERTY_EAX20BUFFER_DIRECT: + call.set_value<Exception>(props.lDirect); + break; -void ALsource::eax_api_get_source_all( - const EaxCall& call) -{ - switch (call.get_version()) - { - case 2: - eax_api_get_source_all_v2(call); + case DSPROPERTY_EAX20BUFFER_DIRECTHF: + call.set_value<Exception>(props.lDirectHF); break; - case 3: - case 4: - eax_api_get_source_all_v3(call); + case DSPROPERTY_EAX20BUFFER_ROOM: + call.set_value<Exception>(props.lRoom); break; - case 5: - eax_api_get_source_all_v5(call); + case DSPROPERTY_EAX20BUFFER_ROOMHF: + call.set_value<Exception>(props.lRoomHF); break; - default: - eax_fail("Unsupported EAX version."); - } -} + case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR: + call.set_value<Exception>(props.flRoomRolloffFactor); + break; -void ALsource::eax_api_get_source_all_obstruction( - const EaxCall& call) -{ - auto eax_obstruction_all = EAXOBSTRUCTIONPROPERTIES{}; - eax_obstruction_all.lObstruction = eax_.source.lObstruction; - eax_obstruction_all.flObstructionLFRatio = eax_.source.flObstructionLFRatio; + case DSPROPERTY_EAX20BUFFER_OBSTRUCTION: + call.set_value<Exception>(props.lObstruction); + break; - call.set_value<EaxSourceException>(eax_obstruction_all); -} + case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO: + call.set_value<Exception>(props.flObstructionLFRatio); + break; -void ALsource::eax_api_get_source_all_occlusion( - const EaxCall& call) -{ - auto eax_occlusion_all = EAXOCCLUSIONPROPERTIES{}; - eax_occlusion_all.lOcclusion = eax_.source.lOcclusion; - eax_occlusion_all.flOcclusionLFRatio = eax_.source.flOcclusionLFRatio; - eax_occlusion_all.flOcclusionRoomRatio = eax_.source.flOcclusionRoomRatio; - eax_occlusion_all.flOcclusionDirectRatio = eax_.source.flOcclusionDirectRatio; + case DSPROPERTY_EAX20BUFFER_OCCLUSION: + call.set_value<Exception>(props.lOcclusion); + break; - call.set_value<EaxSourceException>(eax_occlusion_all); -} + case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO: + call.set_value<Exception>(props.flOcclusionLFRatio); + break; -void ALsource::eax_api_get_source_all_exclusion( - const EaxCall& call) -{ - auto eax_exclusion_all = EAXEXCLUSIONPROPERTIES{}; - eax_exclusion_all.lExclusion = eax_.source.lExclusion; - eax_exclusion_all.flExclusionLFRatio = eax_.source.flExclusionLFRatio; + case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO: + call.set_value<Exception>(props.flOcclusionRoomRatio); + break; - call.set_value<EaxSourceException>(eax_exclusion_all); -} + case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF: + call.set_value<Exception>(props.lOutsideVolumeHF); + break; -void ALsource::eax_api_get_source_active_fx_slot_id( - const EaxCall& call) -{ - switch (call.get_version()) - { - case 4: - { - const auto& active_fx_slots = reinterpret_cast<const EAX40ACTIVEFXSLOTS&>(eax_.active_fx_slots); - call.set_value<EaxSourceException>(active_fx_slots); - } + case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR: + call.set_value<Exception>(props.flAirAbsorptionFactor); break; - case 5: - { - const auto& active_fx_slots = reinterpret_cast<const EAX50ACTIVEFXSLOTS&>(eax_.active_fx_slots); - call.set_value<EaxSourceException>(active_fx_slots); - } + case DSPROPERTY_EAX20BUFFER_FLAGS: + call.set_value<Exception>(props.dwFlags); break; default: - eax_fail("Unsupported EAX version."); + eax_fail_unknown_property_id(); } } -void ALsource::eax_api_get_source_all_2d( - const EaxCall& call) +void ALsource::eax3_get_obstruction(const EaxCall& call, const Eax3Props& props) { - auto eax_2d_all = EAXSOURCE2DPROPERTIES{}; - eax_2d_all.lDirect = eax_.source.lDirect; - eax_2d_all.lDirectHF = eax_.source.lDirectHF; - eax_2d_all.lRoom = eax_.source.lRoom; - eax_2d_all.lRoomHF = eax_.source.lRoomHF; - eax_2d_all.ulFlags = eax_.source.ulFlags; - - call.set_value<EaxSourceException>(eax_2d_all); + const auto& subprops = reinterpret_cast<const EAXOBSTRUCTIONPROPERTIES&>(props.lObstruction); + call.set_value<Exception>(subprops); } -void ALsource::eax_api_get_source_speaker_level_all( - const EaxCall& call) +void ALsource::eax3_get_occlusion(const EaxCall& call, const Eax3Props& props) { - auto& all = call.get_value<EaxSourceException, EAXSPEAKERLEVELPROPERTIES>(); - - eax_validate_source_speaker_id(all.lSpeakerID); - const auto speaker_index = static_cast<std::size_t>(all.lSpeakerID - 1); - all.lLevel = eax_.speaker_levels[speaker_index]; + const auto& subprops = reinterpret_cast<const EAXOCCLUSIONPROPERTIES&>(props.lOcclusion); + call.set_value<Exception>(subprops); } -void ALsource::eax_get( - const EaxCall& call) +void ALsource::eax3_get_exclusion(const EaxCall& call, const Eax3Props& props) { - const auto version = call.get_version(); - - if (version == 1) - { - eax1_get(call); - return; - } - - const auto property_id = (version == 2 ? eax2_translate_property_id(call) : call.get_property_id()); + const auto& subprops = reinterpret_cast<const EAXEXCLUSIONPROPERTIES&>(props.lExclusion); + call.set_value<Exception>(subprops); +} - switch (property_id) - { +void ALsource::eax3_get(const EaxCall& call, const Eax3Props& props) +{ + switch (call.get_property_id()) { case EAXSOURCE_NONE: break; case EAXSOURCE_ALLPARAMETERS: - eax_api_get_source_all(call); + call.set_value<Exception>(props); break; case EAXSOURCE_OBSTRUCTIONPARAMETERS: - eax_api_get_source_all_obstruction(call); + eax3_get_obstruction(call, props); break; case EAXSOURCE_OCCLUSIONPARAMETERS: - eax_api_get_source_all_occlusion(call); + eax3_get_occlusion(call, props); break; case EAXSOURCE_EXCLUSIONPARAMETERS: - eax_api_get_source_all_exclusion(call); + eax3_get_exclusion(call, props); break; case EAXSOURCE_DIRECT: - call.set_value<EaxSourceException>(eax_.source.lDirect); + call.set_value<Exception>(props.lDirect); break; case EAXSOURCE_DIRECTHF: - call.set_value<EaxSourceException>(eax_.source.lDirectHF); + call.set_value<Exception>(props.lDirectHF); break; case EAXSOURCE_ROOM: - call.set_value<EaxSourceException>(eax_.source.lRoom); + call.set_value<Exception>(props.lRoom); break; case EAXSOURCE_ROOMHF: - call.set_value<EaxSourceException>(eax_.source.lRoomHF); + call.set_value<Exception>(props.lRoomHF); break; case EAXSOURCE_OBSTRUCTION: - call.set_value<EaxSourceException>(eax_.source.lObstruction); + call.set_value<Exception>(props.lObstruction); break; case EAXSOURCE_OBSTRUCTIONLFRATIO: - call.set_value<EaxSourceException>(eax_.source.flObstructionLFRatio); + call.set_value<Exception>(props.flObstructionLFRatio); break; case EAXSOURCE_OCCLUSION: - call.set_value<EaxSourceException>(eax_.source.lOcclusion); + call.set_value<Exception>(props.lOcclusion); break; case EAXSOURCE_OCCLUSIONLFRATIO: - call.set_value<EaxSourceException>(eax_.source.flOcclusionLFRatio); + call.set_value<Exception>(props.flOcclusionLFRatio); break; case EAXSOURCE_OCCLUSIONROOMRATIO: - call.set_value<EaxSourceException>(eax_.source.flOcclusionRoomRatio); + call.set_value<Exception>(props.flOcclusionRoomRatio); break; case EAXSOURCE_OCCLUSIONDIRECTRATIO: - call.set_value<EaxSourceException>(eax_.source.flOcclusionDirectRatio); + call.set_value<Exception>(props.flOcclusionDirectRatio); break; case EAXSOURCE_EXCLUSION: - call.set_value<EaxSourceException>(eax_.source.lExclusion); + call.set_value<Exception>(props.lExclusion); break; case EAXSOURCE_EXCLUSIONLFRATIO: - call.set_value<EaxSourceException>(eax_.source.flExclusionLFRatio); + call.set_value<Exception>(props.flExclusionLFRatio); break; case EAXSOURCE_OUTSIDEVOLUMEHF: - call.set_value<EaxSourceException>(eax_.source.lOutsideVolumeHF); + call.set_value<Exception>(props.lOutsideVolumeHF); break; case EAXSOURCE_DOPPLERFACTOR: - call.set_value<EaxSourceException>(eax_.source.flDopplerFactor); + call.set_value<Exception>(props.flDopplerFactor); break; case EAXSOURCE_ROLLOFFFACTOR: - call.set_value<EaxSourceException>(eax_.source.flRolloffFactor); + call.set_value<Exception>(props.flRolloffFactor); break; case EAXSOURCE_ROOMROLLOFFFACTOR: - call.set_value<EaxSourceException>(eax_.source.flRoomRolloffFactor); + call.set_value<Exception>(props.flRoomRolloffFactor); + break; + + case EAXSOURCE_AIRABSORPTIONFACTOR: + call.set_value<Exception>(props.flAirAbsorptionFactor); + break; + + case EAXSOURCE_FLAGS: + call.set_value<Exception>(props.ulFlags); + break; + + default: + eax_fail_unknown_property_id(); + } +} + +void ALsource::eax4_get(const EaxCall& call, const Eax4Props& props) +{ + switch (call.get_property_id()) { + case EAXSOURCE_NONE: break; + case EAXSOURCE_ALLPARAMETERS: + case EAXSOURCE_OBSTRUCTIONPARAMETERS: + case EAXSOURCE_OCCLUSIONPARAMETERS: + case EAXSOURCE_EXCLUSIONPARAMETERS: + case EAXSOURCE_DIRECT: + case EAXSOURCE_DIRECTHF: + case EAXSOURCE_ROOM: + case EAXSOURCE_ROOMHF: + case EAXSOURCE_OBSTRUCTION: + case EAXSOURCE_OBSTRUCTIONLFRATIO: + case EAXSOURCE_OCCLUSION: + case EAXSOURCE_OCCLUSIONLFRATIO: + case EAXSOURCE_OCCLUSIONROOMRATIO: + case EAXSOURCE_OCCLUSIONDIRECTRATIO: + case EAXSOURCE_EXCLUSION: + case EAXSOURCE_EXCLUSIONLFRATIO: + case EAXSOURCE_OUTSIDEVOLUMEHF: + case EAXSOURCE_DOPPLERFACTOR: + case EAXSOURCE_ROLLOFFFACTOR: + case EAXSOURCE_ROOMROLLOFFFACTOR: case EAXSOURCE_AIRABSORPTIONFACTOR: - call.set_value<EaxSourceException>(eax_.source.flAirAbsorptionFactor); + case EAXSOURCE_FLAGS: + eax3_get(call, props.source); break; + case EAXSOURCE_SENDPARAMETERS: + eax_get_sends<EAXSOURCESENDPROPERTIES>(call, props.sends); + break; + + case EAXSOURCE_ALLSENDPARAMETERS: + eax_get_sends<EAXSOURCEALLSENDPROPERTIES>(call, props.sends); + break; + + case EAXSOURCE_OCCLUSIONSENDPARAMETERS: + eax_get_sends<EAXSOURCEOCCLUSIONSENDPROPERTIES>(call, props.sends); + break; + + case EAXSOURCE_EXCLUSIONSENDPARAMETERS: + eax_get_sends<EAXSOURCEEXCLUSIONSENDPROPERTIES>(call, props.sends); + break; + + case EAXSOURCE_ACTIVEFXSLOTID: + call.set_value<Exception>(props.active_fx_slots); + break; + + default: + eax_fail_unknown_property_id(); + } +} + +void ALsource::eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props) +{ + auto& subprops = call.get_value<Exception, EAXSOURCE2DPROPERTIES>(); + subprops.lDirect = props.lDirect; + subprops.lDirectHF = props.lDirectHF; + subprops.lRoom = props.lRoom; + subprops.lRoomHF = props.lRoomHF; + subprops.ulFlags = props.ulFlags; +} + +void ALsource::eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props) +{ + const auto subprops = call.get_values<EAXSPEAKERLEVELPROPERTIES>(eax_max_speakers); + std::uninitialized_copy_n(props.cbegin(), subprops.size(), subprops.begin()); +} + +void ALsource::eax5_get(const EaxCall& call, const Eax5Props& props) +{ + switch (call.get_property_id()) { + case EAXSOURCE_NONE: + break; + + case EAXSOURCE_ALLPARAMETERS: + case EAXSOURCE_OBSTRUCTIONPARAMETERS: + case EAXSOURCE_OCCLUSIONPARAMETERS: + case EAXSOURCE_EXCLUSIONPARAMETERS: + case EAXSOURCE_DIRECT: + case EAXSOURCE_DIRECTHF: + case EAXSOURCE_ROOM: + case EAXSOURCE_ROOMHF: + case EAXSOURCE_OBSTRUCTION: + case EAXSOURCE_OBSTRUCTIONLFRATIO: + case EAXSOURCE_OCCLUSION: + case EAXSOURCE_OCCLUSIONLFRATIO: + case EAXSOURCE_OCCLUSIONROOMRATIO: + case EAXSOURCE_OCCLUSIONDIRECTRATIO: + case EAXSOURCE_EXCLUSION: + case EAXSOURCE_EXCLUSIONLFRATIO: + case EAXSOURCE_OUTSIDEVOLUMEHF: + case EAXSOURCE_DOPPLERFACTOR: + case EAXSOURCE_ROLLOFFFACTOR: + case EAXSOURCE_ROOMROLLOFFFACTOR: + case EAXSOURCE_AIRABSORPTIONFACTOR: case EAXSOURCE_FLAGS: - call.set_value<EaxSourceException>(eax_.source.ulFlags); + eax3_get(call, props.source); break; case EAXSOURCE_SENDPARAMETERS: - eax_api_get_send_properties<EaxSourceException, EAXSOURCESENDPROPERTIES>(call); + eax_get_sends<EAXSOURCESENDPROPERTIES>(call, props.sends); break; case EAXSOURCE_ALLSENDPARAMETERS: - eax_api_get_send_properties<EaxSourceException, EAXSOURCEALLSENDPROPERTIES>(call); + eax_get_sends<EAXSOURCEALLSENDPROPERTIES>(call, props.sends); break; case EAXSOURCE_OCCLUSIONSENDPARAMETERS: - eax_api_get_send_properties<EaxSourceException, EAXSOURCEOCCLUSIONSENDPROPERTIES>(call); + eax_get_sends<EAXSOURCEOCCLUSIONSENDPROPERTIES>(call, props.sends); break; case EAXSOURCE_EXCLUSIONSENDPARAMETERS: - eax_api_get_send_properties<EaxSourceException, EAXSOURCEEXCLUSIONSENDPROPERTIES>(call); + eax_get_sends<EAXSOURCEEXCLUSIONSENDPROPERTIES>(call, props.sends); break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_api_get_source_active_fx_slot_id(call); + call.set_value<Exception>(props.active_fx_slots); break; case EAXSOURCE_MACROFXFACTOR: - call.set_value<EaxSourceException>(eax_.source.flMacroFXFactor); + call.set_value<Exception>(props.source.flMacroFXFactor); break; case EAXSOURCE_SPEAKERLEVELS: - eax_api_get_source_speaker_level_all(call); + call.set_value<Exception>(props.speaker_levels); break; case EAXSOURCE_ALL2DPARAMETERS: - eax_api_get_source_all_2d(call); + eax5_get_all_2d(call, props.source); break; default: - eax_fail("Unsupported property id."); + eax_fail_unknown_property_id(); + } +} + +void ALsource::eax_get(const EaxCall& call) +{ + switch (call.get_version()) { + case 1: eax1_get(call, eax1_.i); break; + case 2: eax2_get(call, eax2_.i); break; + case 3: eax3_get(call, eax3_.i); break; + case 4: eax4_get(call, eax4_.i); break; + case 5: eax5_get(call, eax5_.i); break; + default: eax_fail_unknown_version(); } } -void ALsource::eax_set_al_source_send( - ALeffectslot *slot, - size_t sendidx, - const EaxAlLowPassParam &filter) +void ALsource::eax_set_al_source_send(ALeffectslot *slot, size_t sendidx, const EaxAlLowPassParam &filter) { if(sendidx >= EAX_MAX_FXSLOTS) return; @@ -6088,12 +4851,113 @@ void ALsource::eax_set_al_source_send( send.GainLF = 1.0f; send.LFReference = HIGHPASSFREQREF; - if(slot) IncrementRef(slot->ref); + if(slot != nullptr) + IncrementRef(slot->ref); if(auto *oldslot = send.Slot) DecrementRef(oldslot->ref); - send.Slot = slot; + send.Slot = slot; mPropsDirty = true; } +void ALsource::eax_commit_active_fx_slots() +{ + // Mark all slots as non-active. + eax_active_fx_slots_.fill(false); + + // Mark primary FX slot as active. + if (eax_primary_fx_slot_id_.has_value()) + eax_active_fx_slots_[*eax_primary_fx_slot_id_] = true; + + // Mark the other FX slots as active. + for (const auto& slot_id : eax_.active_fx_slots.guidActiveFXSlots) { + if (slot_id == EAXPROPERTYID_EAX50_FXSlot0) + eax_active_fx_slots_[0] = true; + else if (slot_id == EAXPROPERTYID_EAX50_FXSlot1) + eax_active_fx_slots_[1] = true; + else if (slot_id == EAXPROPERTYID_EAX50_FXSlot2) + eax_active_fx_slots_[2] = true; + else if (slot_id == EAXPROPERTYID_EAX50_FXSlot3) + eax_active_fx_slots_[3] = true; + } + + // Deactivate EFX auxiliary effect slots. + for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) { + if (!eax_active_fx_slots_[i]) + eax_set_al_source_send(nullptr, i, EaxAlLowPassParam{1.0f, 1.0f}); + } +} + +void ALsource::eax_commit_filters() +{ + eax_update_direct_filter(); + eax_update_room_filters(); +} + +void ALsource::eax_commit(EaxCommitType commit_type) +{ + const auto primary_fx_slot_id = eax_al_context_->eax_get_primary_fx_slot_index(); + const auto is_primary_fx_slot_id_changed = (eax_primary_fx_slot_id_ == primary_fx_slot_id); + + const auto is_forced = ( + eax_is_version_changed_ || + is_primary_fx_slot_id_changed || + commit_type == EaxCommitType::forced); + + eax_is_version_changed_ = false; + eax_primary_fx_slot_id_ = primary_fx_slot_id; + + switch (eax_version_) { + case 1: + if (!is_forced && eax1_.i == eax1_.d) + return; + eax1_.i = eax1_.d; + eax1_translate(eax1_.i, eax_); + break; + + case 2: + if (!is_forced && eax2_.i == eax2_.d) + return; + eax2_.i = eax2_.d; + eax2_translate(eax2_.i, eax_); + break; + + case 3: + if (!is_forced && eax3_.i == eax3_.d) + return; + eax3_.i = eax3_.d; + eax3_translate(eax3_.i, eax_); + break; + + case 4: + if (!is_forced && eax4_.i == eax4_.d) + return; + eax4_.i = eax4_.d; + eax4_translate(eax4_.i, eax_); + break; + + case 5: + if (!is_forced && eax5_.i == eax5_.d) + return; + eax5_.i = eax5_.d; + eax_ = eax5_.d; + break; + + default: + eax_fail_unknown_version(); + } + + eax_set_efx_outer_gain_hf(); + eax_set_efx_doppler_factor(); + eax_set_efx_rolloff_factor(); + eax_set_efx_room_rolloff_factor(); + eax_set_efx_air_absorption_factor(); + eax_set_efx_dry_gain_hf_auto(); + eax_set_efx_wet_gain_auto(); + eax_set_efx_wet_gain_hf_auto(); + + eax_commit_active_fx_slots(); + eax_commit_filters(); +} + #endif // ALSOFT_EAX diff --git a/al/source.h b/al/source.h index 2d93e177..850c31aa 100644 --- a/al/source.h +++ b/al/source.h @@ -23,6 +23,7 @@ #ifdef ALSOFT_EAX #include "eax/call.h" +#include "eax/exception.h" #include "eax/fx_slot_index.h" #include "eax/utils.h" #endif // ALSOFT_EAX @@ -48,67 +49,12 @@ struct ALbufferQueueItem : public VoiceBufferItem { #ifdef ALSOFT_EAX -using EaxSourceSourceFilterDirtyFlagsValue = std::uint_least16_t; - -struct EaxSourceSourceFilterDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxSourceSourceFilterDirtyFlagsValue lDirect : 1; - EaxSourceSourceFilterDirtyFlagsValue lDirectHF : 1; - EaxSourceSourceFilterDirtyFlagsValue lRoom : 1; - EaxSourceSourceFilterDirtyFlagsValue lRoomHF : 1; - EaxSourceSourceFilterDirtyFlagsValue lObstruction : 1; - EaxSourceSourceFilterDirtyFlagsValue flObstructionLFRatio : 1; - EaxSourceSourceFilterDirtyFlagsValue lOcclusion : 1; - EaxSourceSourceFilterDirtyFlagsValue flOcclusionLFRatio : 1; - EaxSourceSourceFilterDirtyFlagsValue flOcclusionRoomRatio : 1; - EaxSourceSourceFilterDirtyFlagsValue flOcclusionDirectRatio : 1; - EaxSourceSourceFilterDirtyFlagsValue lExclusion : 1; - EaxSourceSourceFilterDirtyFlagsValue flExclusionLFRatio : 1; -}; // EaxSourceSourceFilterDirtyFlags - - -using EaxSourceSourceMiscDirtyFlagsValue = std::uint_least8_t; - -struct EaxSourceSourceMiscDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxSourceSourceMiscDirtyFlagsValue lOutsideVolumeHF : 1; - EaxSourceSourceMiscDirtyFlagsValue flDopplerFactor : 1; - EaxSourceSourceMiscDirtyFlagsValue flRolloffFactor : 1; - EaxSourceSourceMiscDirtyFlagsValue flRoomRolloffFactor : 1; - EaxSourceSourceMiscDirtyFlagsValue flAirAbsorptionFactor : 1; - EaxSourceSourceMiscDirtyFlagsValue ulFlags : 1; - EaxSourceSourceMiscDirtyFlagsValue flMacroFXFactor : 1; - EaxSourceSourceMiscDirtyFlagsValue speaker_levels : 1; -}; // EaxSourceSourceMiscDirtyFlags - - -using EaxSourceSendDirtyFlagsValue = std::uint_least8_t; - -struct EaxSourceSendDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxSourceSendDirtyFlagsValue lSend : 1; - EaxSourceSendDirtyFlagsValue lSendHF : 1; - EaxSourceSendDirtyFlagsValue lOcclusion : 1; - EaxSourceSendDirtyFlagsValue flOcclusionLFRatio : 1; - EaxSourceSendDirtyFlagsValue flOcclusionRoomRatio : 1; - EaxSourceSendDirtyFlagsValue flOcclusionDirectRatio : 1; - EaxSourceSendDirtyFlagsValue lExclusion : 1; - EaxSourceSendDirtyFlagsValue flExclusionLFRatio : 1; -}; // EaxSourceSendDirtyFlags - - -struct EaxSourceSendsDirtyFlags -{ - using EaxIsBitFieldStruct = bool; - - EaxSourceSendDirtyFlags sends[EAX_MAX_FXSLOTS]; -}; // EaxSourceSendsDirtyFlags +class EaxSourceException : public EaxException { +public: + explicit EaxSourceException(const char* message) + : EaxException{"EAX_SOURCE", message} + {} +}; #endif // ALSOFT_EAX struct ALsource { @@ -214,594 +160,895 @@ struct ALsource { #ifdef ALSOFT_EAX public: void eax_initialize(ALCcontext *context) noexcept; - - - void eax_dispatch(const EaxCall& call) - { call.is_get() ? eax_get(call) : eax_set(call); } - - - void eax_update_filters(); - - void eax_update( - EaxContextSharedDirtyFlags dirty_flags); - - void eax_commit() { eax_apply_deferred(); } + void eax_dispatch(const EaxCall& call); + void eax_commit(); void eax_commit_and_update(); + bool eax_is_initialized() const noexcept { return eax_al_context_ != nullptr; } - bool eax_is_initialized() const noexcept { return eax_al_context_; } - - - static ALsource* eax_lookup_source( - ALCcontext& al_context, - ALuint source_id) noexcept; - + static ALsource* eax_lookup_source(ALCcontext& al_context, ALuint source_id) noexcept; private: - static constexpr auto eax_max_speakers = 9; - - - using EaxActiveFxSlots = std::array<bool, EAX_MAX_FXSLOTS>; - using EaxSpeakerLevels = std::array<long, eax_max_speakers>; - - struct Eax - { - using Sends = std::array<EAXSOURCEALLSENDPROPERTIES, EAX_MAX_FXSLOTS>; - - EAX50ACTIVEFXSLOTS active_fx_slots{}; - EAX50SOURCEPROPERTIES source{}; - Sends sends{}; - EaxSpeakerLevels speaker_levels{}; - }; // Eax - - - bool eax_uses_primary_id_{}; - bool eax_has_active_fx_slots_{}; - bool eax_are_active_fx_slots_dirty_{}; - - ALCcontext* eax_al_context_{}; - - EAXBUFFER_REVERBPROPERTIES eax1_{}; - Eax eax_{}; - Eax eax_d_{}; - EaxActiveFxSlots eax_active_fx_slots_{}; - - EaxSourceSendsDirtyFlags eax_sends_dirty_flags_{}; - EaxSourceSourceFilterDirtyFlags eax_source_dirty_filter_flags_{}; - EaxSourceSourceMiscDirtyFlags eax_source_dirty_misc_flags_{}; - - - [[noreturn]] - static void eax_fail( - const char* message); - - - void eax_set_source_defaults() noexcept; - void eax_set_active_fx_slots_defaults() noexcept; - void eax_set_send_defaults(EAXSOURCEALLSENDPROPERTIES& eax_send) noexcept; - void eax_set_sends_defaults() noexcept; - void eax_set_speaker_levels_defaults() noexcept; - void eax_set_defaults() noexcept; - - - static float eax_calculate_dst_occlusion_mb( - long src_occlusion_mb, - float path_ratio, - float lf_ratio) noexcept; - - EaxAlLowPassParam eax_create_direct_filter_param() const noexcept; - - EaxAlLowPassParam eax_create_room_filter_param( - const ALeffectslot& fx_slot, - const EAXSOURCEALLSENDPROPERTIES& send) const noexcept; - - void eax_set_fx_slots(); - - void eax_initialize_fx_slots(); - - void eax_update_direct_filter_internal(); - - void eax_update_room_filters_internal(); - - void eax_update_filters_internal(); - - void eax_update_primary_fx_slot_id(); - - - void eax_defer_active_fx_slots( - const EaxCall& call); - - - static const char* eax_get_exclusion_name() noexcept; - - static const char* eax_get_exclusion_lf_ratio_name() noexcept; - - - static const char* eax_get_occlusion_name() noexcept; - - static const char* eax_get_occlusion_lf_ratio_name() noexcept; - - static const char* eax_get_occlusion_direct_ratio_name() noexcept; - - static const char* eax_get_occlusion_room_ratio_name() noexcept; - - - static void eax1_validate_reverb_mix(float reverb_mix); - - static void eax_validate_send_receiving_fx_slot_guid( - const GUID& guidReceivingFXSlotID); - - static void eax_validate_send_send( - long lSend); - - static void eax_validate_send_send_hf( - long lSendHF); - - static void eax_validate_send_occlusion( - long lOcclusion); - - static void eax_validate_send_occlusion_lf_ratio( - float flOcclusionLFRatio); - - static void eax_validate_send_occlusion_room_ratio( - float flOcclusionRoomRatio); - - static void eax_validate_send_occlusion_direct_ratio( - float flOcclusionDirectRatio); - - static void eax_validate_send_exclusion( - long lExclusion); - - static void eax_validate_send_exclusion_lf_ratio( - float flExclusionLFRatio); - - static void eax_validate_send( - const EAXSOURCESENDPROPERTIES& all); - - static void eax_validate_send_exclusion_all( - const EAXSOURCEEXCLUSIONSENDPROPERTIES& all); - - static void eax_validate_send_occlusion_all( - const EAXSOURCEOCCLUSIONSENDPROPERTIES& all); - - static void eax_validate_send_all( - const EAXSOURCEALLSENDPROPERTIES& all); - - - static EaxFxSlotIndexValue eax_get_send_index( - const GUID& send_guid); - - - void eax_defer_send_send( - long lSend, - EaxFxSlotIndexValue index); - - void eax_defer_send_send_hf( - long lSendHF, - EaxFxSlotIndexValue index); - - void eax_defer_send_occlusion( - long lOcclusion, - EaxFxSlotIndexValue index); - - void eax_defer_send_occlusion_lf_ratio( - float flOcclusionLFRatio, - EaxFxSlotIndexValue index); - - void eax_defer_send_occlusion_room_ratio( - float flOcclusionRoomRatio, - EaxFxSlotIndexValue index); - - void eax_defer_send_occlusion_direct_ratio( - float flOcclusionDirectRatio, - EaxFxSlotIndexValue index); - - void eax_defer_send_exclusion( - long lExclusion, - EaxFxSlotIndexValue index); - - void eax_defer_send_exclusion_lf_ratio( - float flExclusionLFRatio, - EaxFxSlotIndexValue index); - - void eax_defer_send( - const EAXSOURCESENDPROPERTIES& all, - EaxFxSlotIndexValue index); - - void eax_defer_send_exclusion_all( - const EAXSOURCEEXCLUSIONSENDPROPERTIES& all, - EaxFxSlotIndexValue index); - - void eax_defer_send_occlusion_all( - const EAXSOURCEOCCLUSIONSENDPROPERTIES& all, - EaxFxSlotIndexValue index); - - void eax_defer_send_all( - const EAXSOURCEALLSENDPROPERTIES& all, - EaxFxSlotIndexValue index); - - - void eax_defer_send( - const EaxCall& call); - - void eax_defer_send_exclusion_all( - const EaxCall& call); - - void eax_defer_send_occlusion_all( - const EaxCall& call); - - void eax_defer_send_all( - const EaxCall& call); - - - static void eax_validate_source_direct( - long direct); - - static void eax_validate_source_direct_hf( - long direct_hf); - - static void eax_validate_source_room( - long room); - - static void eax_validate_source_room_hf( - long room_hf); - - static void eax_validate_source_obstruction( - long obstruction); - - static void eax_validate_source_obstruction_lf_ratio( - float obstruction_lf_ratio); - - static void eax_validate_source_occlusion( - long occlusion); - - static void eax_validate_source_occlusion_lf_ratio( - float occlusion_lf_ratio); - - static void eax_validate_source_occlusion_room_ratio( - float occlusion_room_ratio); - - static void eax_validate_source_occlusion_direct_ratio( - float occlusion_direct_ratio); - - static void eax_validate_source_exclusion( - long exclusion); - - static void eax_validate_source_exclusion_lf_ratio( - float exclusion_lf_ratio); - - static void eax_validate_source_outside_volume_hf( - long outside_volume_hf); - - static void eax_validate_source_doppler_factor( - float doppler_factor); - - static void eax_validate_source_rolloff_factor( - float rolloff_factor); - - static void eax_validate_source_room_rolloff_factor( - float room_rolloff_factor); - - static void eax_validate_source_air_absorption_factor( - float air_absorption_factor); - - static void eax_validate_source_flags( - unsigned long flags, - int eax_version); - - static void eax_validate_source_macro_fx_factor( - float macro_fx_factor); - - static void eax_validate_source_2d_all( - const EAXSOURCE2DPROPERTIES& all, - int eax_version); - - static void eax_validate_source_obstruction_all( - const EAXOBSTRUCTIONPROPERTIES& all); - - static void eax_validate_source_exclusion_all( - const EAXEXCLUSIONPROPERTIES& all); - - static void eax_validate_source_occlusion_all( - const EAXOCCLUSIONPROPERTIES& all); - - static void eax_validate_source_all( - const EAX20BUFFERPROPERTIES& all, - int eax_version); - - static void eax_validate_source_all( - const EAX30SOURCEPROPERTIES& all, - int eax_version); - - static void eax_validate_source_all( - const EAX50SOURCEPROPERTIES& all, - int eax_version); - - static void eax_validate_source_speaker_id( - long speaker_id); - - static void eax_validate_source_speaker_level( - long speaker_level); - - static void eax_validate_source_speaker_level_all( - const EAXSPEAKERLEVELPROPERTIES& all); + using Exception = EaxSourceException; + enum class EaxCommitType { + normal, + forced, + }; - void eax_defer_source_direct( - long lDirect); + static constexpr auto eax_max_speakers = 9; - void eax_defer_source_direct_hf( - long lDirectHF); + using EaxFxSlotIds = const GUID* [EAX_MAX_FXSLOTS]; - void eax_defer_source_room( - long lRoom); + static constexpr const EaxFxSlotIds eax4_fx_slot_ids = { + &EAXPROPERTYID_EAX40_FXSlot0, + &EAXPROPERTYID_EAX40_FXSlot1, + &EAXPROPERTYID_EAX40_FXSlot2, + &EAXPROPERTYID_EAX40_FXSlot3, + }; - void eax_defer_source_room_hf( - long lRoomHF); + static constexpr const EaxFxSlotIds eax5_fx_slot_ids = { + &EAXPROPERTYID_EAX50_FXSlot0, + &EAXPROPERTYID_EAX50_FXSlot1, + &EAXPROPERTYID_EAX50_FXSlot2, + &EAXPROPERTYID_EAX50_FXSlot3, + }; - void eax_defer_source_obstruction( - long lObstruction); + using EaxActiveFxSlots = std::array<bool, EAX_MAX_FXSLOTS>; + using EaxSpeakerLevels = std::array<EAXSPEAKERLEVELPROPERTIES, eax_max_speakers>; + using EaxSends = std::array<EAXSOURCEALLSENDPROPERTIES, EAX_MAX_FXSLOTS>; - void eax_defer_source_obstruction_lf_ratio( - float flObstructionLFRatio); + using Eax1Props = EAXBUFFER_REVERBPROPERTIES; - void eax_defer_source_occlusion( - long lOcclusion); + struct Eax1State { + Eax1Props i; // Immediate. + Eax1Props d; // Deferred. + }; - void eax_defer_source_occlusion_lf_ratio( - float flOcclusionLFRatio); + using Eax2Props = EAX20BUFFERPROPERTIES; - void eax_defer_source_occlusion_room_ratio( - float flOcclusionRoomRatio); + struct Eax2State { + Eax2Props i; // Immediate. + Eax2Props d; // Deferred. + }; - void eax_defer_source_occlusion_direct_ratio( - float flOcclusionDirectRatio); + using Eax3Props = EAX30SOURCEPROPERTIES; - void eax_defer_source_exclusion( - long lExclusion); + struct Eax3State { + Eax3Props i; // Immediate. + Eax3Props d; // Deferred. + }; - void eax_defer_source_exclusion_lf_ratio( - float flExclusionLFRatio); + struct Eax4Props { + Eax3Props source; + EaxSends sends; + EAX40ACTIVEFXSLOTS active_fx_slots; - void eax_defer_source_outside_volume_hf( - long lOutsideVolumeHF); + bool operator==(const Eax4Props& rhs) noexcept + { + return std::memcmp(this, &rhs, sizeof(Eax4Props)) == 0; + } + }; - void eax_defer_source_doppler_factor( - float flDopplerFactor); + struct Eax4State { + Eax4Props i; // Immediate. + Eax4Props d; // Deferred. + }; - void eax_defer_source_rolloff_factor( - float flRolloffFactor); + struct Eax5Props { + EAX50SOURCEPROPERTIES source; + EaxSends sends; + EAX50ACTIVEFXSLOTS active_fx_slots; + EaxSpeakerLevels speaker_levels; - void eax_defer_source_room_rolloff_factor( - float flRoomRolloffFactor); + bool operator==(const Eax5Props& rhs) noexcept + { + return std::memcmp(this, &rhs, sizeof(Eax5Props)) == 0; + } + }; - void eax_defer_source_air_absorption_factor( - float flAirAbsorptionFactor); + struct Eax5State { + Eax5Props i; // Immediate. + Eax5Props d; // Deferred. + }; - void eax_defer_source_flags( - unsigned long ulFlags); + ALCcontext* eax_al_context_{}; + EaxFxSlotIndex eax_primary_fx_slot_id_{}; + EaxActiveFxSlots eax_active_fx_slots_{}; + bool eax_is_version_changed_{}; + int eax_version_{}; + Eax1State eax1_{}; + Eax2State eax2_{}; + Eax3State eax3_{}; + Eax4State eax4_{}; + Eax5State eax5_{}; + Eax5Props eax_{}; + + // ---------------------------------------------------------------------- + // Source validators + + struct Eax1SourceReverbMixValidator { + void operator()(float reverb_mix) const + { + if (reverb_mix == EAX_REVERBMIX_USEDISTANCE) + return; + + eax_validate_range<Exception>( + "Reverb Mix", + reverb_mix, + EAX_BUFFER_MINREVERBMIX, + EAX_BUFFER_MAXREVERBMIX); + } + }; - void eax_defer_source_macro_fx_factor( - float flMacroFXFactor); + struct Eax2SourceDirectValidator { + void operator()(long lDirect) const + { + eax_validate_range<Exception>( + "Direct", + lDirect, + EAXSOURCE_MINDIRECT, + EAXSOURCE_MAXDIRECT); + } + }; - void eax_defer_source_2d_all( - const EAXSOURCE2DPROPERTIES& all); + struct Eax2SourceDirectHfValidator { + void operator()(long lDirectHF) const + { + eax_validate_range<Exception>( + "Direct HF", + lDirectHF, + EAXSOURCE_MINDIRECTHF, + EAXSOURCE_MAXDIRECTHF); + } + }; - void eax_defer_source_obstruction_all( - const EAXOBSTRUCTIONPROPERTIES& all); + struct Eax2SourceRoomValidator { + void operator()(long lRoom) const + { + eax_validate_range<Exception>( + "Room", + lRoom, + EAXSOURCE_MINROOM, + EAXSOURCE_MAXROOM); + } + }; - void eax_defer_source_exclusion_all( - const EAXEXCLUSIONPROPERTIES& all); + struct Eax2SourceRoomHfValidator { + void operator()(long lRoomHF) const + { + eax_validate_range<Exception>( + "Room HF", + lRoomHF, + EAXSOURCE_MINROOMHF, + EAXSOURCE_MAXROOMHF); + } + }; - void eax_defer_source_occlusion_all( - const EAXOCCLUSIONPROPERTIES& all); + struct Eax2SourceRoomRolloffFactorValidator { + void operator()(float flRoomRolloffFactor) const + { + eax_validate_range<Exception>( + "Room Rolloff Factor", + flRoomRolloffFactor, + EAXSOURCE_MINROOMROLLOFFFACTOR, + EAXSOURCE_MAXROOMROLLOFFFACTOR); + } + }; - void eax_defer_source_all( - const EAX20BUFFERPROPERTIES& all); + struct Eax2SourceObstructionValidator { + void operator()(long lObstruction) const + { + eax_validate_range<Exception>( + "Obstruction", + lObstruction, + EAXSOURCE_MINOBSTRUCTION, + EAXSOURCE_MAXOBSTRUCTION); + } + }; - void eax_defer_source_all( - const EAX30SOURCEPROPERTIES& all); + struct Eax2SourceObstructionLfRatioValidator { + void operator()(float flObstructionLFRatio) const + { + eax_validate_range<Exception>( + "Obstruction LF Ratio", + flObstructionLFRatio, + EAXSOURCE_MINOBSTRUCTIONLFRATIO, + EAXSOURCE_MAXOBSTRUCTIONLFRATIO); + } + }; - void eax_defer_source_all( - const EAX50SOURCEPROPERTIES& all); + struct Eax2SourceOcclusionValidator { + void operator()(long lOcclusion) const + { + eax_validate_range<Exception>( + "Occlusion", + lOcclusion, + EAXSOURCE_MINOCCLUSION, + EAXSOURCE_MAXOCCLUSION); + } + }; - void eax_defer_source_speaker_level_all( - const EAXSPEAKERLEVELPROPERTIES& all); + struct Eax2SourceOcclusionLfRatioValidator { + void operator()(float flOcclusionLFRatio) const + { + eax_validate_range<Exception>( + "Occlusion LF Ratio", + flOcclusionLFRatio, + EAXSOURCE_MINOCCLUSIONLFRATIO, + EAXSOURCE_MAXOCCLUSIONLFRATIO); + } + }; + struct Eax2SourceOcclusionRoomRatioValidator { + void operator()(float flOcclusionRoomRatio) const + { + eax_validate_range<Exception>( + "Occlusion Room Ratio", + flOcclusionRoomRatio, + EAXSOURCE_MINOCCLUSIONROOMRATIO, + EAXSOURCE_MAXOCCLUSIONROOMRATIO); + } + }; - void eax_defer_source_direct( - const EaxCall& call); + struct Eax2SourceOutsideVolumeHfValidator { + void operator()(long lOutsideVolumeHF) const + { + eax_validate_range<Exception>( + "Outside Volume HF", + lOutsideVolumeHF, + EAXSOURCE_MINOUTSIDEVOLUMEHF, + EAXSOURCE_MAXOUTSIDEVOLUMEHF); + } + }; - void eax_defer_source_direct_hf( - const EaxCall& call); + struct Eax2SourceAirAbsorptionFactorValidator { + void operator()(float flAirAbsorptionFactor) const + { + eax_validate_range<Exception>( + "Air Absorption Factor", + flAirAbsorptionFactor, + EAXSOURCE_MINAIRABSORPTIONFACTOR, + EAXSOURCE_MAXAIRABSORPTIONFACTOR); + } + }; - void eax_defer_source_room( - const EaxCall& call); + struct Eax2SourceFlagsValidator { + void operator()(unsigned long dwFlags) const + { + eax_validate_range<Exception>( + "Flags", + dwFlags, + 0UL, + ~EAX20SOURCEFLAGS_RESERVED); + } + }; - void eax_defer_source_room_hf( - const EaxCall& call); + struct Eax3SourceOcclusionDirectRatioValidator { + void operator()(float flOcclusionDirectRatio) const + { + eax_validate_range<Exception>( + "Occlusion Direct Ratio", + flOcclusionDirectRatio, + EAXSOURCE_MINOCCLUSIONDIRECTRATIO, + EAXSOURCE_MAXOCCLUSIONDIRECTRATIO); + } + }; - void eax_defer_source_obstruction( - const EaxCall& call); + struct Eax3SourceExclusionValidator { + void operator()(long lExclusion) const + { + eax_validate_range<Exception>( + "Exclusion", + lExclusion, + EAXSOURCE_MINEXCLUSION, + EAXSOURCE_MAXEXCLUSION); + } + }; - void eax_defer_source_obstruction_lf_ratio( - const EaxCall& call); + struct Eax3SourceExclusionLfRatioValidator { + void operator()(float flExclusionLFRatio) const + { + eax_validate_range<Exception>( + "Exclusion LF Ratio", + flExclusionLFRatio, + EAXSOURCE_MINEXCLUSIONLFRATIO, + EAXSOURCE_MAXEXCLUSIONLFRATIO); + } + }; - void eax_defer_source_occlusion( - const EaxCall& call); + struct Eax3SourceDopplerFactorValidator { + void operator()(float flDopplerFactor) const + { + eax_validate_range<Exception>( + "Doppler Factor", + flDopplerFactor, + EAXSOURCE_MINDOPPLERFACTOR, + EAXSOURCE_MAXDOPPLERFACTOR); + } + }; - void eax_defer_source_occlusion_lf_ratio( - const EaxCall& call); + struct Eax3SourceRolloffFactorValidator { + void operator()(float flRolloffFactor) const + { + eax_validate_range<Exception>( + "Rolloff Factor", + flRolloffFactor, + EAXSOURCE_MINROLLOFFFACTOR, + EAXSOURCE_MAXROLLOFFFACTOR); + } + }; - void eax_defer_source_occlusion_room_ratio( - const EaxCall& call); + struct Eax5SourceMacroFXFactorValidator { + void operator()(float flMacroFXFactor) const + { + eax_validate_range<Exception>( + "Macro FX Factor", + flMacroFXFactor, + EAXSOURCE_MINMACROFXFACTOR, + EAXSOURCE_MAXMACROFXFACTOR); + } + }; - void eax_defer_source_occlusion_direct_ratio( - const EaxCall& call); + struct Eax5SourceFlagsValidator { + void operator()(unsigned long dwFlags) const + { + eax_validate_range<Exception>( + "Flags", + dwFlags, + 0UL, + ~EAX50SOURCEFLAGS_RESERVED); + } + }; - void eax_defer_source_exclusion( - const EaxCall& call); + struct Eax1SourceAllValidator { + void operator()(const Eax1Props& props) const + { + Eax1SourceReverbMixValidator{}(props.fMix); + } + }; - void eax_defer_source_exclusion_lf_ratio( - const EaxCall& call); + struct Eax2SourceAllValidator { + void operator()(const Eax2Props& props) const + { + Eax2SourceDirectValidator{}(props.lDirect); + Eax2SourceDirectHfValidator{}(props.lDirectHF); + Eax2SourceRoomValidator{}(props.lRoom); + Eax2SourceRoomHfValidator{}(props.lRoomHF); + Eax2SourceRoomRolloffFactorValidator{}(props.flRoomRolloffFactor); + Eax2SourceObstructionValidator{}(props.lObstruction); + Eax2SourceObstructionLfRatioValidator{}(props.flObstructionLFRatio); + Eax2SourceOcclusionValidator{}(props.lOcclusion); + Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio); + Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio); + Eax2SourceOutsideVolumeHfValidator{}(props.lOutsideVolumeHF); + Eax2SourceAirAbsorptionFactorValidator{}(props.flAirAbsorptionFactor); + Eax2SourceFlagsValidator{}(props.dwFlags); + } + }; - void eax_defer_source_outside_volume_hf( - const EaxCall& call); + struct Eax3SourceAllValidator { + void operator()(const Eax3Props& props) const + { + Eax2SourceDirectValidator{}(props.lDirect); + Eax2SourceDirectHfValidator{}(props.lDirectHF); + Eax2SourceRoomValidator{}(props.lRoom); + Eax2SourceRoomHfValidator{}(props.lRoomHF); + Eax2SourceObstructionValidator{}(props.lObstruction); + Eax2SourceObstructionLfRatioValidator{}(props.flObstructionLFRatio); + Eax2SourceOcclusionValidator{}(props.lOcclusion); + Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio); + Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio); + Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio); + Eax3SourceExclusionValidator{}(props.lExclusion); + Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio); + Eax2SourceOutsideVolumeHfValidator{}(props.lOutsideVolumeHF); + Eax3SourceDopplerFactorValidator{}(props.flDopplerFactor); + Eax3SourceRolloffFactorValidator{}(props.flRolloffFactor); + Eax2SourceRoomRolloffFactorValidator{}(props.flRoomRolloffFactor); + Eax2SourceAirAbsorptionFactorValidator{}(props.flAirAbsorptionFactor); + Eax2SourceFlagsValidator{}(props.ulFlags); + } + }; - void eax_defer_source_doppler_factor( - const EaxCall& call); + struct Eax5SourceAllValidator { + void operator()(const EAX50SOURCEPROPERTIES& props) const + { + Eax3SourceAllValidator{}(static_cast<const Eax3Props&>(props)); + Eax5SourceMacroFXFactorValidator{}(props.flMacroFXFactor); + } + }; - void eax_defer_source_rolloff_factor( - const EaxCall& call); + struct Eax5SourceAll2dValidator { + void operator()(const EAXSOURCE2DPROPERTIES& props) const + { + Eax2SourceDirectValidator{}(props.lDirect); + Eax2SourceDirectHfValidator{}(props.lDirectHF); + Eax2SourceRoomValidator{}(props.lRoom); + Eax2SourceRoomHfValidator{}(props.lRoomHF); + Eax5SourceFlagsValidator{}(props.ulFlags); + } + }; - void eax_defer_source_room_rolloff_factor( - const EaxCall& call); + struct Eax4ObstructionValidator { + void operator()(const EAXOBSTRUCTIONPROPERTIES& props) const + { + Eax2SourceObstructionValidator{}(props.lObstruction); + Eax2SourceObstructionLfRatioValidator{}(props.flObstructionLFRatio); + } + }; - void eax_defer_source_air_absorption_factor( - const EaxCall& call); + struct Eax4OcclusionValidator { + void operator()(const EAXOCCLUSIONPROPERTIES& props) const + { + Eax2SourceOcclusionValidator{}(props.lOcclusion); + Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio); + Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio); + Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio); + } + }; - void eax_defer_source_flags( - const EaxCall& call); + struct Eax4ExclusionValidator { + void operator()(const EAXEXCLUSIONPROPERTIES& props) const + { + Eax3SourceExclusionValidator{}(props.lExclusion); + Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio); + } + }; - void eax_defer_source_macro_fx_factor( - const EaxCall& call); + // Source validators + // ---------------------------------------------------------------------- + // Send validators - void eax_defer_source_2d_all( - const EaxCall& call); + struct Eax4SendReceivingFxSlotIdValidator { + void operator()(const GUID& guidReceivingFXSlotID) const + { + if (guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX40_FXSlot3) + { + eax_fail_unknown_receiving_fx_slot_id(); + } + } + }; - void eax_defer_source_obstruction_all( - const EaxCall& call); + struct Eax5SendReceivingFxSlotIdValidator { + void operator()(const GUID& guidReceivingFXSlotID) const + { + if (guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 && + guidReceivingFXSlotID != EAXPROPERTYID_EAX50_FXSlot3) + { + eax_fail_unknown_receiving_fx_slot_id(); + } + } + }; - void eax_defer_source_exclusion_all( - const EaxCall& call); + struct Eax4SendSendValidator { + void operator()(long lSend) const + { + eax_validate_range<Exception>( + "Send", + lSend, + EAXSOURCE_MINSEND, + EAXSOURCE_MAXSEND); + } + }; - void eax_defer_source_occlusion_all( - const EaxCall& call); + struct Eax4SendSendHfValidator { + void operator()(long lSendHF) const + { + eax_validate_range<Exception>( + "Send HF", + lSendHF, + EAXSOURCE_MINSENDHF, + EAXSOURCE_MAXSENDHF); + } + }; - void eax_defer_source_all( - const EaxCall& call); + template<typename TIdValidator> + struct EaxSendValidator { + void operator()(const EAXSOURCESENDPROPERTIES& props) const + { + TIdValidator{}(props.guidReceivingFXSlotID); + Eax4SendSendValidator{}(props.lSend); + Eax4SendSendHfValidator{}(props.lSendHF); + } + }; - void eax_defer_source_speaker_level_all( - const EaxCall& call); + struct Eax4SendValidator : EaxSendValidator<Eax4SendReceivingFxSlotIdValidator> {}; + struct Eax5SendValidator : EaxSendValidator<Eax5SendReceivingFxSlotIdValidator> {}; + template<typename TIdValidator> + struct EaxOcclusionSendValidator { + void operator()(const EAXSOURCEOCCLUSIONSENDPROPERTIES& props) const + { + TIdValidator{}(props.guidReceivingFXSlotID); + Eax2SourceOcclusionValidator{}(props.lOcclusion); + Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio); + Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio); + Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio); + } + }; - void eax_set_outside_volume_hf(); + struct Eax4OcclusionSendValidator : EaxOcclusionSendValidator<Eax4SendReceivingFxSlotIdValidator> {}; + struct Eax5OcclusionSendValidator : EaxOcclusionSendValidator<Eax5SendReceivingFxSlotIdValidator> {}; - void eax_set_doppler_factor(); + template<typename TIdValidator> + struct EaxExclusionSendValidator { + void operator()(const EAXSOURCEEXCLUSIONSENDPROPERTIES& props) const + { + TIdValidator{}(props.guidReceivingFXSlotID); + Eax3SourceExclusionValidator{}(props.lExclusion); + Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio); + } + }; - void eax_set_rolloff_factor(); + struct Eax4ExclusionSendValidator : EaxExclusionSendValidator<Eax4SendReceivingFxSlotIdValidator> {}; + struct Eax5ExclusionSendValidator : EaxExclusionSendValidator<Eax5SendReceivingFxSlotIdValidator> {}; - void eax_set_room_rolloff_factor(); + template<typename TIdValidator> + struct EaxAllSendValidator { + void operator()(const EAXSOURCEALLSENDPROPERTIES& props) const + { + TIdValidator{}(props.guidReceivingFXSlotID); + Eax4SendSendValidator{}(props.lSend); + Eax4SendSendHfValidator{}(props.lSendHF); + Eax2SourceOcclusionValidator{}(props.lOcclusion); + Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio); + Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio); + Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio); + Eax3SourceExclusionValidator{}(props.lExclusion); + Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio); + } + }; - void eax_set_air_absorption_factor(); + struct Eax4AllSendValidator : EaxAllSendValidator<Eax4SendReceivingFxSlotIdValidator> {}; + struct Eax5AllSendValidator : EaxAllSendValidator<Eax5SendReceivingFxSlotIdValidator> {}; + // Send validators + // ---------------------------------------------------------------------- + // Active FX slot ID validators - void eax_set_direct_hf_auto_flag(); + struct Eax4ActiveFxSlotIdValidator { + void operator()(const GUID& id) const + { + if (id != EAX_NULL_GUID && + id != EAX_PrimaryFXSlotID && + id != EAXPROPERTYID_EAX40_FXSlot0 && + id != EAXPROPERTYID_EAX40_FXSlot1 && + id != EAXPROPERTYID_EAX40_FXSlot2 && + id != EAXPROPERTYID_EAX40_FXSlot3) + { + eax_fail_unknown_active_fx_slot_id(); + } + } + }; - void eax_set_room_auto_flag(); + struct Eax5ActiveFxSlotIdValidator { + void operator()(const GUID& id) const + { + if (id != EAX_NULL_GUID && + id != EAX_PrimaryFXSlotID && + id != EAXPROPERTYID_EAX50_FXSlot0 && + id != EAXPROPERTYID_EAX50_FXSlot1 && + id != EAXPROPERTYID_EAX50_FXSlot2 && + id != EAXPROPERTYID_EAX50_FXSlot3) + { + eax_fail_unknown_active_fx_slot_id(); + } + } + }; - void eax_set_room_hf_auto_flag(); + // Active FX slot ID validators + // ---------------------------------------------------------------------- + // Speaker level validators. - void eax_set_flags(); + struct Eax5SpeakerIdValidator { + void operator()(long lSpeakerID) const + { + switch (lSpeakerID) { + case EAXSPEAKER_FRONT_LEFT: + case EAXSPEAKER_FRONT_CENTER: + case EAXSPEAKER_FRONT_RIGHT: + case EAXSPEAKER_SIDE_RIGHT: + case EAXSPEAKER_REAR_RIGHT: + case EAXSPEAKER_REAR_CENTER: + case EAXSPEAKER_REAR_LEFT: + case EAXSPEAKER_SIDE_LEFT: + case EAXSPEAKER_LOW_FREQUENCY: + break; + + default: + eax_fail("Unknown speaker ID."); + } + } + }; + struct Eax5SpeakerLevelValidator { + void operator()(long lLevel) const + { + // TODO Use a range when the feature will be implemented. + if (lLevel != EAXSOURCE_DEFAULTSPEAKERLEVEL) + eax_fail("Speaker level out of range."); + } + }; - void eax_set_macro_fx_factor(); + struct Eax5SpeakerAllValidator { + void operator()(const EAXSPEAKERLEVELPROPERTIES& all) const + { + Eax5SpeakerIdValidator{}(all.lSpeakerID); + Eax5SpeakerLevelValidator{}(all.lLevel); + } + }; - void eax_set_speaker_levels(); + // Speaker level validators. + // ---------------------------------------------------------------------- - static ALuint eax2_translate_property_id(const EaxCall& call); + struct Eax4SendIndexGetter { + EaxFxSlotIndexValue operator()(const GUID& id) const + { + if (id == EAXPROPERTYID_EAX40_FXSlot0) + return 0; + if (id == EAXPROPERTYID_EAX40_FXSlot1) + return 1; + if (id == EAXPROPERTYID_EAX40_FXSlot2) + return 2; + if (id == EAXPROPERTYID_EAX40_FXSlot3) + return 3; + eax_fail_unknown_receiving_fx_slot_id(); + } + }; - void eax1_set_efx(); - void eax1_set_reverb_mix(const EaxCall& call); - void eax1_set(const EaxCall& call); + struct Eax5SendIndexGetter { + EaxFxSlotIndexValue operator()(const GUID& id) const + { + if (id == EAXPROPERTYID_EAX50_FXSlot0) + return 0; + if (id == EAXPROPERTYID_EAX50_FXSlot1) + return 1; + if (id == EAXPROPERTYID_EAX50_FXSlot2) + return 2; + if (id == EAXPROPERTYID_EAX50_FXSlot3) + return 3; + eax_fail_unknown_receiving_fx_slot_id(); + } + }; - void eax_apply_deferred(); + [[noreturn]] static void eax_fail(const char* message); + [[noreturn]] static void eax_fail_unknown_property_id(); + [[noreturn]] static void eax_fail_unknown_version(); + [[noreturn]] static void eax_fail_unknown_active_fx_slot_id(); + [[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id(); + + void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept; + void eax1_set_defaults(Eax1Props& props) noexcept; + void eax1_set_defaults() noexcept; + void eax2_set_defaults(Eax2Props& props) noexcept; + void eax2_set_defaults() noexcept; + void eax3_set_defaults(Eax3Props& props) noexcept; + void eax3_set_defaults() noexcept; + void eax4_set_sends_defaults(EaxSends& sends) noexcept; + void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept; + void eax4_set_defaults() noexcept; + void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept; + void eax5_set_sends_defaults(EaxSends& sends) noexcept; + void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept; + void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept; + void eax5_set_defaults(Eax5Props& props) noexcept; + void eax5_set_defaults() noexcept; + void eax_set_defaults() noexcept; - void eax_set( - const EaxCall& call); + void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept; + void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept; + void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept; + void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept; + static float eax_calculate_dst_occlusion_mb( + long src_occlusion_mb, + float path_ratio, + float lf_ratio) noexcept; - static const GUID& eax_get_send_fx_slot_guid( - int eax_version, - EaxFxSlotIndexValue fx_slot_index); + EaxAlLowPassParam eax_create_direct_filter_param() const noexcept; - static void eax_copy_send( - const EAXSOURCEALLSENDPROPERTIES& src_send, - EAXSOURCESENDPROPERTIES& dst_send); + EaxAlLowPassParam eax_create_room_filter_param( + const ALeffectslot& fx_slot, + const EAXSOURCEALLSENDPROPERTIES& send) const noexcept; - static void eax_copy_send( - const EAXSOURCEALLSENDPROPERTIES& src_send, - EAXSOURCEALLSENDPROPERTIES& dst_send); + void eax_update_direct_filter(); + void eax_update_room_filters(); + void eax_commit_filters(); - static void eax_copy_send( - const EAXSOURCEALLSENDPROPERTIES& src_send, - EAXSOURCEOCCLUSIONSENDPROPERTIES& dst_send); + static void eax_copy_send_for_get( + const EAXSOURCEALLSENDPROPERTIES& src, + EAXSOURCESENDPROPERTIES& dst) noexcept + { + dst = reinterpret_cast<const EAXSOURCESENDPROPERTIES&>(src); + } - static void eax_copy_send( - const EAXSOURCEALLSENDPROPERTIES& src_send, - EAXSOURCEEXCLUSIONSENDPROPERTIES& dst_send); + static void eax_copy_send_for_get( + const EAXSOURCEALLSENDPROPERTIES& src, + EAXSOURCEALLSENDPROPERTIES& dst) noexcept + { + dst = src; + } - template< - typename TException, - typename TSrcSend - > - void eax_api_get_send_properties( - const EaxCall& call) const + static void eax_copy_send_for_get( + const EAXSOURCEALLSENDPROPERTIES& src, + EAXSOURCEOCCLUSIONSENDPROPERTIES& dst) noexcept { - const auto eax_version = call.get_version(); - const auto dst_sends = call.get_values<TException, TSrcSend>(); - const auto send_count = dst_sends.size(); + dst.guidReceivingFXSlotID = src.guidReceivingFXSlotID; + dst.lOcclusion = src.lOcclusion; + dst.flOcclusionLFRatio = src.flOcclusionLFRatio; + dst.flOcclusionRoomRatio = src.flOcclusionRoomRatio; + dst.flOcclusionDirectRatio = src.flOcclusionDirectRatio; + } - for (auto fx_slot_index = EaxFxSlotIndexValue{}; fx_slot_index < send_count; ++fx_slot_index) - { - auto& dst_send = dst_sends[fx_slot_index]; - const auto& src_send = eax_.sends[fx_slot_index]; + static void eax_copy_send_for_get( + const EAXSOURCEALLSENDPROPERTIES& src, + EAXSOURCEEXCLUSIONSENDPROPERTIES& dst) noexcept + { + dst.guidReceivingFXSlotID = src.guidReceivingFXSlotID; + dst.lExclusion = src.lExclusion; + dst.flExclusionLFRatio = src.flExclusionLFRatio; + } - eax_copy_send(src_send, dst_send); + template<typename TDstSend> + void eax_get_sends(const EaxCall& call, const EaxSends& src_sends) + { + const auto dst_sends = call.get_values<TDstSend>(EAX_MAX_FXSLOTS); + const auto count = dst_sends.size(); - dst_send.guidReceivingFXSlotID = eax_get_send_fx_slot_guid(eax_version, fx_slot_index); + for (auto i = decltype(count){}; i < count; ++i) { + const auto& src_send = src_sends[i]; + auto& dst_send = dst_sends[i]; + eax_copy_send_for_get(src_send, dst_send); } } + void eax1_get(const EaxCall& call, const Eax1Props& props); + void eax2_get(const EaxCall& call, const Eax2Props& props); + void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props); + void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props); + void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props); + void eax3_get(const EaxCall& call, const Eax3Props& props); + void eax4_get(const EaxCall& call, const Eax4Props& props); + void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props); + void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props); + void eax5_get(const EaxCall& call, const Eax5Props& props); + void eax_get(const EaxCall& call); + + static void eax_copy_send_for_set( + const EAXSOURCESENDPROPERTIES& src, + EAXSOURCEALLSENDPROPERTIES& dst) noexcept + { + dst.lSend = src.lSend; + dst.lSendHF = src.lSendHF; + } - void eax1_get(const EaxCall& call); - - void eax_api_get_source_all_v2( - const EaxCall& call); + static void eax_copy_send_for_set( + const EAXSOURCEALLSENDPROPERTIES& src, + EAXSOURCEALLSENDPROPERTIES& dst) noexcept + { + dst.lSend = src.lSend; + dst.lSendHF = src.lSendHF; + dst.lOcclusion = src.lOcclusion; + dst.flOcclusionLFRatio = src.flOcclusionLFRatio; + dst.flOcclusionRoomRatio = src.flOcclusionRoomRatio; + dst.flOcclusionDirectRatio = src.flOcclusionDirectRatio; + dst.lExclusion = src.lExclusion; + dst.flExclusionLFRatio = src.flExclusionLFRatio; + } - void eax_api_get_source_all_v3( - const EaxCall& call); + static void eax_copy_send_for_set( + const EAXSOURCEOCCLUSIONSENDPROPERTIES& src, + EAXSOURCEALLSENDPROPERTIES& dst) noexcept + { + dst.lOcclusion = src.lOcclusion; + dst.flOcclusionLFRatio = src.flOcclusionLFRatio; + dst.flOcclusionRoomRatio = src.flOcclusionRoomRatio; + dst.flOcclusionDirectRatio = src.flOcclusionDirectRatio; + } - void eax_api_get_source_all_v5( - const EaxCall& call); + static void eax_copy_send_for_set( + const EAXSOURCEEXCLUSIONSENDPROPERTIES& src, + EAXSOURCEALLSENDPROPERTIES& dst) noexcept + { + dst.lExclusion = src.lExclusion; + dst.flExclusionLFRatio = src.flExclusionLFRatio; + } - void eax_api_get_source_all( - const EaxCall& call); + template<typename TValidator, typename TIndexGetter, typename TSrcSend> + void eax_defer_sends(const EaxCall& call, EaxSends& dst_sends) + { + const auto src_sends = call.get_values<const TSrcSend>(EAX_MAX_FXSLOTS); + std::for_each(src_sends.cbegin(), src_sends.cend(), TValidator{}); + const auto count = src_sends.size(); + const auto index_getter = TIndexGetter{}; + + for (auto i = decltype(count){}; i < count; ++i) { + const auto& src_send = src_sends[i]; + const auto dst_index = index_getter(src_send.guidReceivingFXSlotID); + auto& dst_send = dst_sends[dst_index]; + eax_copy_send_for_set(src_send, dst_send); + } + } - void eax_api_get_source_all_obstruction( - const EaxCall& call); + template<typename TValidator, typename TSrcSend> + void eax4_defer_sends(const EaxCall& call, EaxSends& dst_sends) + { + eax_defer_sends<TValidator, Eax4SendIndexGetter, TSrcSend>(call, dst_sends); + } - void eax_api_get_source_all_occlusion( - const EaxCall& call); + template<typename TValidator, typename TSrcSend> + void eax5_defer_sends(const EaxCall& call, EaxSends& dst_sends) + { + eax_defer_sends<TValidator, Eax5SendIndexGetter, TSrcSend>(call, dst_sends); + } - void eax_api_get_source_all_exclusion( - const EaxCall& call); + template<typename TValidator, size_t TIdCount> + void eax_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + { + const auto src_ids = call.get_values<const GUID>(TIdCount); + std::for_each(src_ids.cbegin(), src_ids.cend(), TValidator{}); + std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids); + } - void eax_api_get_source_active_fx_slot_id( - const EaxCall& call); + template<size_t TIdCount> + void eax4_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + { + eax_defer_active_fx_slot_id<Eax4ActiveFxSlotIdValidator>(call, dst_ids); + } - void eax_api_get_source_all_2d( - const EaxCall& call); + template<size_t TIdCount> + void eax5_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + { + eax_defer_active_fx_slot_id<Eax5ActiveFxSlotIdValidator>(call, dst_ids); + } - void eax_api_get_source_speaker_level_all( - const EaxCall& call); + template<typename TValidator, typename TProperty> + static void eax_defer(const EaxCall& call, TProperty& property) + { + const auto& value = call.get_value<Exception, const TProperty>(); + TValidator{}(value); + property = value; + } - void eax_get( - const EaxCall& call); + // Defers source's sub-properties (obstruction, occlusion, exclusion). + template<typename TValidator, typename TSubproperty, typename TProperty> + void eax_defer_sub(const EaxCall& call, TProperty& property) + { + const auto& src_props = call.get_value<Exception, const TSubproperty>(); + TValidator{}(src_props); + auto& dst_props = reinterpret_cast<TSubproperty&>(property); + dst_props = src_props; + } + void eax_set_efx_outer_gain_hf(); + void eax_set_efx_doppler_factor(); + void eax_set_efx_rolloff_factor(); + void eax_set_efx_room_rolloff_factor(); + void eax_set_efx_air_absorption_factor(); + void eax_set_efx_dry_gain_hf_auto(); + void eax_set_efx_wet_gain_auto(); + void eax_set_efx_wet_gain_hf_auto(); + + void eax1_set(const EaxCall& call, Eax1Props& props); + void eax2_set(const EaxCall& call, Eax2Props& props); + void eax3_set(const EaxCall& call, Eax3Props& props); + void eax4_set(const EaxCall& call, Eax4Props& props); + void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props); + void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props); + void eax5_set(const EaxCall& call, Eax5Props& props); + void eax_set(const EaxCall& call); // `alSource3i(source, AL_AUXILIARY_SEND_FILTER, ...)` void eax_set_al_source_send(ALeffectslot *slot, size_t sendidx, const EaxAlLowPassParam &filter); + + void eax_commit_active_fx_slots(); + void eax_commit(EaxCommitType commit_type); #endif // ALSOFT_EAX }; |