15 #include "al/auxeffectslot.h"
16 #include "al/source.h"
17 #include "al/effect.h"
19 #include "al/listener.h"
22 #include "core/async_event.h"
23 #include "core/device.h"
24 #include "core/effectslot.h"
25 #include "core/logging.h"
26 #include "core/voice.h"
27 #include "core/voice_change.h"
29 #include "ringbuffer.h"
35 #include "al/eax/globals.h"
40 using namespace std::placeholders
;
44 /* Default context extensions */
45 constexpr ALchar alExtList
[] =
49 "AL_EXT_EXPONENT_DISTANCE "
52 "AL_EXT_LINEAR_DISTANCE "
55 "AL_EXT_MULAW_BFORMAT "
56 "AL_EXT_MULAW_MCFORMATS "
58 "AL_EXT_source_distance_model "
59 "AL_EXT_SOURCE_RADIUS "
60 "AL_EXT_STEREO_ANGLES "
61 "AL_LOKI_quadriphonic "
63 "AL_SOFTX_bformat_hoa "
64 "AL_SOFT_block_alignment "
65 "AL_SOFT_callback_buffer "
66 "AL_SOFTX_convolution_reverb "
67 "AL_SOFT_deferred_updates "
68 "AL_SOFT_direct_channels "
69 "AL_SOFT_direct_channels_remix "
70 "AL_SOFT_effect_target "
72 "AL_SOFT_gain_clamp_ex "
73 "AL_SOFTX_hold_on_disconnect "
74 "AL_SOFT_loop_points "
75 "AL_SOFTX_map_buffer "
77 "AL_SOFT_source_latency "
78 "AL_SOFT_source_length "
79 "AL_SOFT_source_resampler "
80 "AL_SOFT_source_spatialize "
81 "AL_SOFTX_source_start_delay "
87 std::atomic
<bool> ALCcontext::sGlobalContextLock
{false};
88 std::atomic
<ALCcontext
*> ALCcontext::sGlobalContext
{nullptr};
90 thread_local ALCcontext
*ALCcontext::sLocalContext
{nullptr};
91 ALCcontext::ThreadCtx::~ThreadCtx()
93 if(ALCcontext
*ctx
{ALCcontext::sLocalContext
})
95 const bool result
{ctx
->releaseIfNoDelete()};
96 ERR("Context %p current for thread being destroyed%s!\n", voidp
{ctx
},
97 result
? "" : ", leak detected");
100 thread_local
ALCcontext::ThreadCtx
ALCcontext::sThreadContext
;
102 ALeffect
ALCcontext::sDefaultEffect
;
106 ALCcontext
*ALCcontext::getThreadContext() noexcept
107 { return sLocalContext
; }
108 void ALCcontext::setThreadContext(ALCcontext
*context
) noexcept
109 { sThreadContext
.set(context
); }
112 ALCcontext::ALCcontext(al::intrusive_ptr
<ALCdevice
> device
)
113 : ContextBase
{device
.get()}, mALDevice
{std::move(device
)}
117 ALCcontext::~ALCcontext()
119 TRACE("Freeing context %p\n", voidp
{this});
121 size_t count
{std::accumulate(mSourceList
.cbegin(), mSourceList
.cend(), size_t{0u},
122 [](size_t cur
, const SourceSubList
&sublist
) noexcept
-> size_t
123 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); })};
125 WARN("%zu Source%s not deleted\n", count
, (count
==1)?"":"s");
133 mDefaultSlot
= nullptr;
134 count
= std::accumulate(mEffectSlotList
.cbegin(), mEffectSlotList
.cend(), size_t{0u},
135 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
136 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); });
138 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count
, (count
==1)?"":"s");
139 mEffectSlotList
.clear();
143 void ALCcontext::init()
145 if(sDefaultEffect
.type
!= AL_EFFECT_NULL
&& mDevice
->Type
== DeviceType::Playback
)
147 mDefaultSlot
= std::make_unique
<ALeffectslot
>(this);
148 aluInitEffectPanning(mDefaultSlot
->mSlot
, this);
151 EffectSlotArray
*auxslots
;
153 auxslots
= EffectSlot::CreatePtrArray(0);
156 auxslots
= EffectSlot::CreatePtrArray(1);
157 (*auxslots
)[0] = mDefaultSlot
->mSlot
;
158 mDefaultSlot
->mState
= SlotState::Playing
;
160 mActiveAuxSlots
.store(auxslots
, std::memory_order_relaxed
);
164 VoiceChange
*cur
{mVoiceChangeTail
};
165 while(VoiceChange
*next
{cur
->mNext
.load(std::memory_order_relaxed
)})
167 mCurrentVoiceChange
.store(cur
, std::memory_order_relaxed
);
170 mExtensionList
= alExtList
;
173 eax_initialize_extensions();
176 mParams
.Position
= alu::Vector
{0.0f
, 0.0f
, 0.0f
, 1.0f
};
177 mParams
.Matrix
= alu::Matrix::Identity();
178 mParams
.Velocity
= alu::Vector
{};
179 mParams
.Gain
= mListener
.Gain
;
180 mParams
.MetersPerUnit
= mListener
.mMetersPerUnit
;
181 mParams
.AirAbsorptionGainHF
= mAirAbsorptionGainHF
;
182 mParams
.DopplerFactor
= mDopplerFactor
;
183 mParams
.SpeedOfSound
= mSpeedOfSound
* mDopplerVelocity
;
184 mParams
.SourceDistanceModel
= mSourceDistanceModel
;
185 mParams
.mDistanceModel
= mDistanceModel
;
188 mAsyncEvents
= RingBuffer::Create(511, sizeof(AsyncEvent
), false);
189 StartEventThrd(this);
193 mActiveVoiceCount
.store(64, std::memory_order_relaxed
);
196 bool ALCcontext::deinit()
198 if(sLocalContext
== this)
200 WARN("%p released while current on thread\n", voidp
{this});
201 sThreadContext
.set(nullptr);
205 ALCcontext
*origctx
{this};
206 if(sGlobalContext
.compare_exchange_strong(origctx
, nullptr))
208 while(sGlobalContextLock
.load()) {
209 /* Wait to make sure another thread didn't get the context and is
210 * trying to increment its refcount.
217 /* First make sure this context exists in the device's list. */
218 auto *oldarray
= mDevice
->mContexts
.load(std::memory_order_acquire
);
219 if(auto toremove
= static_cast<size_t>(std::count(oldarray
->begin(), oldarray
->end(), this)))
221 using ContextArray
= al::FlexArray
<ContextBase
*>;
222 auto alloc_ctx_array
= [](const size_t count
) -> ContextArray
*
224 if(count
== 0) return &DeviceBase::sEmptyContextArray
;
225 return ContextArray::Create(count
).release();
227 auto *newarray
= alloc_ctx_array(oldarray
->size() - toremove
);
229 /* Copy the current/old context handles to the new array, excluding the
232 std::copy_if(oldarray
->begin(), oldarray
->end(), newarray
->begin(),
233 [this](auto a
){ return a
!= this; });
235 /* Store the new context array in the device. Wait for any current mix
236 * to finish before deleting the old array.
238 mDevice
->mContexts
.store(newarray
);
239 if(oldarray
!= &DeviceBase::sEmptyContextArray
)
241 mDevice
->waitForMix();
245 ret
= !newarray
->empty();
248 ret
= !oldarray
->empty();
255 void ALCcontext::applyAllUpdates()
257 /* Tell the mixer to stop applying updates, then wait for any active
258 * updating to finish, before providing updates.
260 mHoldUpdates
.store(true, std::memory_order_release
);
261 while((mUpdateCount
.load(std::memory_order_acquire
)&1) != 0) {
266 if(eax_is_initialized_
)
269 if(std::exchange(mPropsDirty
, false))
270 UpdateContextProps(this);
271 UpdateAllEffectSlotProps(this);
272 UpdateAllSourceProps(this);
274 /* Now with all updates declared, let the mixer continue applying them so
275 * they all happen at once.
277 mHoldUpdates
.store(false, std::memory_order_release
);
284 void ForEachSource(ALCcontext
*context
, F func
)
286 for(auto &sublist
: context
->mSourceList
)
288 uint64_t usemask
{~sublist
.FreeMask
};
291 const int idx
{al::countr_zero(usemask
)};
292 usemask
&= ~(1_u64
<< idx
);
294 func(sublist
.Sources
[idx
]);
302 bool ALCcontext::eax_is_capable() const noexcept
304 return eax_has_enough_aux_sends();
307 void ALCcontext::eax_uninitialize() noexcept
309 if(!eax_is_initialized_
)
312 eax_is_initialized_
= false;
313 eax_is_tried_
= false;
314 eax_fx_slots_
.uninitialize();
317 ALenum
ALCcontext::eax_eax_set(
318 const GUID
* property_set_id
,
320 ALuint property_source_id
,
321 ALvoid
* property_value
,
322 ALuint property_value_size
)
324 const auto call
= create_eax_call(
330 property_value_size
);
332 const auto eax_version
= call
.get_version();
333 if(eax_version
!= eax_version_
)
334 eax_df_
= ~EaxDirtyFlags();
335 eax_version_
= eax_version
;
338 switch(call
.get_property_set_id())
340 case EaxCallPropertySetId::context
:
343 case EaxCallPropertySetId::fx_slot
:
344 case EaxCallPropertySetId::fx_slot_effect
:
345 eax_dispatch_fx_slot(call
);
347 case EaxCallPropertySetId::source
:
348 eax_dispatch_source(call
);
351 eax_fail_unknown_property_set_id();
354 if(!call
.is_deferred() && !mDeferUpdates
)
360 ALenum
ALCcontext::eax_eax_get(
361 const GUID
* property_set_id
,
363 ALuint property_source_id
,
364 ALvoid
* property_value
,
365 ALuint property_value_size
)
367 const auto call
= create_eax_call(
373 property_value_size
);
374 eax_version_
= call
.get_version();
377 switch(call
.get_property_set_id())
379 case EaxCallPropertySetId::context
:
382 case EaxCallPropertySetId::fx_slot
:
383 case EaxCallPropertySetId::fx_slot_effect
:
384 eax_dispatch_fx_slot(call
);
386 case EaxCallPropertySetId::source
:
387 eax_dispatch_source(call
);
390 eax_fail_unknown_property_set_id();
396 void ALCcontext::eax_commit_and_update_sources()
398 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
399 ForEachSource(this, std::mem_fn(&ALsource::eax_commit_and_update
));
402 void ALCcontext::eax_set_last_error() noexcept
404 eax_last_error_
= EAXERR_INVALID_OPERATION
;
407 [[noreturn
]] void ALCcontext::eax_fail(const char* message
)
409 throw ContextException
{message
};
412 [[noreturn
]] void ALCcontext::eax_fail_unknown_property_set_id()
414 eax_fail("Unknown property ID.");
417 [[noreturn
]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
419 eax_fail("Unknown primary FX Slot ID.");
422 [[noreturn
]] void ALCcontext::eax_fail_unknown_property_id()
424 eax_fail("Unknown property ID.");
427 [[noreturn
]] void ALCcontext::eax_fail_unknown_version()
429 eax_fail("Unknown version.");
432 void ALCcontext::eax_initialize_extensions()
434 if(!eax_g_is_enabled
)
437 const auto string_max_capacity
=
438 std::strlen(mExtensionList
) + 1 +
439 std::strlen(eax1_ext_name
) + 1 +
440 std::strlen(eax2_ext_name
) + 1 +
441 std::strlen(eax3_ext_name
) + 1 +
442 std::strlen(eax4_ext_name
) + 1 +
443 std::strlen(eax5_ext_name
) + 1 +
444 std::strlen(eax_x_ram_ext_name
) + 1;
446 eax_extension_list_
.reserve(string_max_capacity
);
448 if(eax_is_capable()) {
449 eax_extension_list_
+= eax1_ext_name
;
450 eax_extension_list_
+= ' ';
452 eax_extension_list_
+= eax2_ext_name
;
453 eax_extension_list_
+= ' ';
455 eax_extension_list_
+= eax3_ext_name
;
456 eax_extension_list_
+= ' ';
458 eax_extension_list_
+= eax4_ext_name
;
459 eax_extension_list_
+= ' ';
461 eax_extension_list_
+= eax5_ext_name
;
462 eax_extension_list_
+= ' ';
465 eax_extension_list_
+= eax_x_ram_ext_name
;
466 eax_extension_list_
+= ' ';
468 eax_extension_list_
+= mExtensionList
;
469 mExtensionList
= eax_extension_list_
.c_str();
472 void ALCcontext::eax_initialize()
474 if(eax_is_initialized_
)
480 eax_is_tried_
= true;
482 if(!eax_g_is_enabled
)
483 eax_fail("EAX disabled by a configuration.");
485 eax_ensure_compatibility();
487 eax_context_commit_air_absorbtion_hf();
488 eax_update_speaker_configuration();
489 eax_initialize_fx_slots();
490 eax_initialize_sources();
492 eax_is_initialized_
= true;
499 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
501 return mDefaultSlot
== nullptr;
504 void ALCcontext::eax_ensure_no_default_effect_slot() const
506 if(!eax_has_no_default_effect_slot())
507 eax_fail("There is a default effect slot in the context.");
510 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
512 return mALDevice
->NumAuxSends
>= EAX_MAX_FXSLOTS
;
515 void ALCcontext::eax_ensure_enough_aux_sends() const
517 if(!eax_has_enough_aux_sends())
518 eax_fail("Not enough aux sends.");
521 void ALCcontext::eax_ensure_compatibility()
523 eax_ensure_enough_aux_sends();
526 unsigned long ALCcontext::eax_detect_speaker_configuration() const
528 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
530 switch(mDevice
->FmtChans
)
532 case DevFmtMono
: return SPEAKERS_2
;
534 /* Pretend 7.1 if using UHJ output, since they both provide full
535 * horizontal surround.
537 if(mDevice
->mUhjEncoder
)
539 if(mDevice
->Flags
.test(DirectEar
))
542 case DevFmtQuad
: return SPEAKERS_4
;
543 case DevFmtX51
: return SPEAKERS_5
;
544 case DevFmtX61
: return SPEAKERS_6
;
545 case DevFmtX71
: return SPEAKERS_7
;
546 /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
547 * suggest with-height surround sound (like HRTF).
549 case DevFmtX714
: return SPEAKERS_7
;
550 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
551 * suggest full-sphere surround sound (like HRTF).
553 case DevFmtX3D71
: return SPEAKERS_5
;
554 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
555 * provide full-sphere surround sound. Depends if apps are more likely to
556 * consider headphones or 7.1 for surround sound support.
558 case DevFmtAmbi3D
: return SPEAKERS_7
;
560 ERR(EAX_PREFIX
"Unexpected device channel format 0x%x.\n", mDevice
->FmtChans
);
566 void ALCcontext::eax_update_speaker_configuration()
568 eax_speaker_config_
= eax_detect_speaker_configuration();
571 void ALCcontext::eax_set_last_error_defaults() noexcept
573 eax_last_error_
= EAX_OK
;
576 void ALCcontext::eax_session_set_defaults() noexcept
578 eax_session_
.ulEAXVersion
= EAXCONTEXT_DEFAULTEAXSESSION
;
579 eax_session_
.ulMaxActiveSends
= EAXCONTEXT_DEFAULTMAXACTIVESENDS
;
582 void ALCcontext::eax4_context_set_defaults(Eax4Props
& props
) noexcept
584 props
.guidPrimaryFXSlotID
= EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID
;
585 props
.flDistanceFactor
= EAXCONTEXT_DEFAULTDISTANCEFACTOR
;
586 props
.flAirAbsorptionHF
= EAXCONTEXT_DEFAULTAIRABSORPTIONHF
;
587 props
.flHFReference
= EAXCONTEXT_DEFAULTHFREFERENCE
;
590 void ALCcontext::eax4_context_set_defaults(Eax4State
& state
) noexcept
592 eax4_context_set_defaults(state
.i
);
596 void ALCcontext::eax5_context_set_defaults(Eax5Props
& props
) noexcept
598 props
.guidPrimaryFXSlotID
= EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID
;
599 props
.flDistanceFactor
= EAXCONTEXT_DEFAULTDISTANCEFACTOR
;
600 props
.flAirAbsorptionHF
= EAXCONTEXT_DEFAULTAIRABSORPTIONHF
;
601 props
.flHFReference
= EAXCONTEXT_DEFAULTHFREFERENCE
;
602 props
.flMacroFXFactor
= EAXCONTEXT_DEFAULTMACROFXFACTOR
;
605 void ALCcontext::eax5_context_set_defaults(Eax5State
& state
) noexcept
607 eax5_context_set_defaults(state
.i
);
611 void ALCcontext::eax4_context_set_current_defaults(const Eax4Props
& props
) noexcept
613 static_cast<Eax4Props
&>(eax_
) = props
;
614 eax_
.flMacroFXFactor
= EAXCONTEXT_DEFAULTMACROFXFACTOR
;
617 void ALCcontext::eax5_context_set_current_defaults(const Eax5Props
& props
) noexcept
622 void ALCcontext::eax_context_set_current_defaults()
629 eax5_context_set_current_defaults(eax123_
.i
);
632 eax4_context_set_current_defaults(eax4_
.i
);
635 eax5_context_set_current_defaults(eax5_
.i
);
638 eax_fail_unknown_version();
641 eax_df_
= ~EaxDirtyFlags
{};
644 void ALCcontext::eax_context_set_defaults()
646 eax5_context_set_defaults(eax123_
);
647 eax4_context_set_defaults(eax4_
);
648 eax5_context_set_defaults(eax5_
);
649 eax_context_set_current_defaults();
652 void ALCcontext::eax_set_defaults()
654 eax_set_last_error_defaults();
655 eax_session_set_defaults();
656 eax_context_set_defaults();
659 void ALCcontext::eax_dispatch_fx_slot(const EaxCall
& call
)
661 const auto fx_slot_index
= call
.get_fx_slot_index();
662 if(!fx_slot_index
.has_value())
663 eax_fail("Invalid fx slot index.");
665 auto& fx_slot
= eax_get_fx_slot(*fx_slot_index
);
666 if(fx_slot
.eax_dispatch(call
))
668 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
669 ForEachSource(this, [](ALsource
& source
){ source
.eax_mark_as_changed(); });
673 void ALCcontext::eax_dispatch_source(const EaxCall
& call
)
675 const auto source_id
= call
.get_property_al_name();
676 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
677 const auto source
= ALsource::eax_lookup_source(*this, source_id
);
679 if (source
== nullptr)
680 eax_fail("Source not found.");
682 source
->eax_dispatch(call
);
685 void ALCcontext::eax_get_misc(const EaxCall
& call
)
687 switch(call
.get_property_id())
689 case EAXCONTEXT_NONE
:
691 case EAXCONTEXT_LASTERROR
:
692 call
.set_value
<ContextException
>(eax_last_error_
);
694 case EAXCONTEXT_SPEAKERCONFIG
:
695 call
.set_value
<ContextException
>(eax_speaker_config_
);
697 case EAXCONTEXT_EAXSESSION
:
698 call
.set_value
<ContextException
>(eax_session_
);
701 eax_fail_unknown_property_id();
705 void ALCcontext::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
707 switch(call
.get_property_id())
709 case EAXCONTEXT_ALLPARAMETERS
:
710 call
.set_value
<ContextException
>(props
);
712 case EAXCONTEXT_PRIMARYFXSLOTID
:
713 call
.set_value
<ContextException
>(props
.guidPrimaryFXSlotID
);
715 case EAXCONTEXT_DISTANCEFACTOR
:
716 call
.set_value
<ContextException
>(props
.flDistanceFactor
);
718 case EAXCONTEXT_AIRABSORPTIONHF
:
719 call
.set_value
<ContextException
>(props
.flAirAbsorptionHF
);
721 case EAXCONTEXT_HFREFERENCE
:
722 call
.set_value
<ContextException
>(props
.flHFReference
);
730 void ALCcontext::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
732 switch(call
.get_property_id())
734 case EAXCONTEXT_ALLPARAMETERS
:
735 call
.set_value
<ContextException
>(props
);
737 case EAXCONTEXT_PRIMARYFXSLOTID
:
738 call
.set_value
<ContextException
>(props
.guidPrimaryFXSlotID
);
740 case EAXCONTEXT_DISTANCEFACTOR
:
741 call
.set_value
<ContextException
>(props
.flDistanceFactor
);
743 case EAXCONTEXT_AIRABSORPTIONHF
:
744 call
.set_value
<ContextException
>(props
.flAirAbsorptionHF
);
746 case EAXCONTEXT_HFREFERENCE
:
747 call
.set_value
<ContextException
>(props
.flHFReference
);
749 case EAXCONTEXT_MACROFXFACTOR
:
750 call
.set_value
<ContextException
>(props
.flMacroFXFactor
);
758 void ALCcontext::eax_get(const EaxCall
& call
)
760 switch(call
.get_version())
762 case 4: eax4_get(call
, eax4_
.i
); break;
763 case 5: eax5_get(call
, eax5_
.i
); break;
764 default: eax_fail_unknown_version();
768 void ALCcontext::eax_context_commit_primary_fx_slot_id()
770 eax_primary_fx_slot_index_
= eax_
.guidPrimaryFXSlotID
;
773 void ALCcontext::eax_context_commit_distance_factor()
775 if(mListener
.mMetersPerUnit
== eax_
.flDistanceFactor
)
778 mListener
.mMetersPerUnit
= eax_
.flDistanceFactor
;
782 void ALCcontext::eax_context_commit_air_absorbtion_hf()
784 const auto new_value
= level_mb_to_gain(eax_
.flAirAbsorptionHF
);
786 if(mAirAbsorptionGainHF
== new_value
)
789 mAirAbsorptionGainHF
= new_value
;
793 void ALCcontext::eax_context_commit_hf_reference()
798 void ALCcontext::eax_context_commit_macro_fx_factor()
803 void ALCcontext::eax_initialize_fx_slots()
805 eax_fx_slots_
.initialize(*this);
806 eax_primary_fx_slot_index_
= eax_
.guidPrimaryFXSlotID
;
809 void ALCcontext::eax_initialize_sources()
811 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
812 auto init_source
= [this](ALsource
&source
) noexcept
813 { source
.eax_initialize(this); };
814 ForEachSource(this, init_source
);
817 void ALCcontext::eax_update_sources()
819 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
820 auto update_source
= [](ALsource
&source
)
821 { source
.eax_commit(); };
822 ForEachSource(this, update_source
);
825 void ALCcontext::eax_set_misc(const EaxCall
& call
)
827 switch(call
.get_property_id())
829 case EAXCONTEXT_NONE
:
831 case EAXCONTEXT_SPEAKERCONFIG
:
832 eax_set
<Eax5SpeakerConfigValidator
>(call
, eax_speaker_config_
);
834 case EAXCONTEXT_EAXSESSION
:
835 eax_set
<Eax5SessionAllValidator
>(call
, eax_session_
);
838 eax_fail_unknown_property_id();
842 void ALCcontext::eax4_defer_all(const EaxCall
& call
, Eax4State
& state
)
844 const auto& src
= call
.get_value
<ContextException
, const EAX40CONTEXTPROPERTIES
>();
845 Eax4AllValidator
{}(src
);
846 const auto& dst_i
= state
.i
;
847 auto& dst_d
= state
.d
;
850 if(dst_i
.guidPrimaryFXSlotID
!= dst_d
.guidPrimaryFXSlotID
)
851 eax_df_
|= eax_primary_fx_slot_id_dirty_bit
;
853 if(dst_i
.flDistanceFactor
!= dst_d
.flDistanceFactor
)
854 eax_df_
|= eax_distance_factor_dirty_bit
;
856 if(dst_i
.flAirAbsorptionHF
!= dst_d
.flAirAbsorptionHF
)
857 eax_df_
|= eax_air_absorption_hf_dirty_bit
;
859 if(dst_i
.flHFReference
!= dst_d
.flHFReference
)
860 eax_df_
|= eax_hf_reference_dirty_bit
;
863 void ALCcontext::eax4_defer(const EaxCall
& call
, Eax4State
& state
)
865 switch(call
.get_property_id())
867 case EAXCONTEXT_ALLPARAMETERS
:
868 eax4_defer_all(call
, state
);
870 case EAXCONTEXT_PRIMARYFXSLOTID
:
871 eax_defer
<Eax4PrimaryFxSlotIdValidator
, eax_primary_fx_slot_id_dirty_bit
>(
872 call
, state
, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
874 case EAXCONTEXT_DISTANCEFACTOR
:
875 eax_defer
<Eax4DistanceFactorValidator
, eax_distance_factor_dirty_bit
>(
876 call
, state
, &EAX40CONTEXTPROPERTIES::flDistanceFactor
);
878 case EAXCONTEXT_AIRABSORPTIONHF
:
879 eax_defer
<Eax4AirAbsorptionHfValidator
, eax_air_absorption_hf_dirty_bit
>(
880 call
, state
, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF
);
882 case EAXCONTEXT_HFREFERENCE
:
883 eax_defer
<Eax4HfReferenceValidator
, eax_hf_reference_dirty_bit
>(
884 call
, state
, &EAX40CONTEXTPROPERTIES::flHFReference
);
892 void ALCcontext::eax5_defer_all(const EaxCall
& call
, Eax5State
& state
)
894 const auto& src
= call
.get_value
<ContextException
, const EAX50CONTEXTPROPERTIES
>();
895 Eax4AllValidator
{}(src
);
896 const auto& dst_i
= state
.i
;
897 auto& dst_d
= state
.d
;
900 if(dst_i
.guidPrimaryFXSlotID
!= dst_d
.guidPrimaryFXSlotID
)
901 eax_df_
|= eax_primary_fx_slot_id_dirty_bit
;
903 if(dst_i
.flDistanceFactor
!= dst_d
.flDistanceFactor
)
904 eax_df_
|= eax_distance_factor_dirty_bit
;
906 if(dst_i
.flAirAbsorptionHF
!= dst_d
.flAirAbsorptionHF
)
907 eax_df_
|= eax_air_absorption_hf_dirty_bit
;
909 if(dst_i
.flHFReference
!= dst_d
.flHFReference
)
910 eax_df_
|= eax_hf_reference_dirty_bit
;
912 if(dst_i
.flMacroFXFactor
!= dst_d
.flMacroFXFactor
)
913 eax_df_
|= eax_macro_fx_factor_dirty_bit
;
916 void ALCcontext::eax5_defer(const EaxCall
& call
, Eax5State
& state
)
918 switch(call
.get_property_id())
920 case EAXCONTEXT_ALLPARAMETERS
:
921 eax5_defer_all(call
, state
);
923 case EAXCONTEXT_PRIMARYFXSLOTID
:
924 eax_defer
<Eax5PrimaryFxSlotIdValidator
, eax_primary_fx_slot_id_dirty_bit
>(
925 call
, state
, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
927 case EAXCONTEXT_DISTANCEFACTOR
:
928 eax_defer
<Eax4DistanceFactorValidator
, eax_distance_factor_dirty_bit
>(
929 call
, state
, &EAX50CONTEXTPROPERTIES::flDistanceFactor
);
931 case EAXCONTEXT_AIRABSORPTIONHF
:
932 eax_defer
<Eax4AirAbsorptionHfValidator
, eax_air_absorption_hf_dirty_bit
>(
933 call
, state
, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF
);
935 case EAXCONTEXT_HFREFERENCE
:
936 eax_defer
<Eax4HfReferenceValidator
, eax_hf_reference_dirty_bit
>(
937 call
, state
, &EAX50CONTEXTPROPERTIES::flHFReference
);
939 case EAXCONTEXT_MACROFXFACTOR
:
940 eax_defer
<Eax5MacroFxFactorValidator
, eax_macro_fx_factor_dirty_bit
>(
941 call
, state
, &EAX50CONTEXTPROPERTIES::flMacroFXFactor
);
949 void ALCcontext::eax_set(const EaxCall
& call
)
951 switch(call
.get_version())
953 case 4: eax4_defer(call
, eax4_
); break;
954 case 5: eax5_defer(call
, eax5_
); break;
955 default: eax_fail_unknown_version();
959 void ALCcontext::eax4_context_commit(Eax4State
& state
, EaxDirtyFlags
& dst_df
)
961 if(eax_df_
== EaxDirtyFlags
{})
964 eax_context_commit_property
<eax_primary_fx_slot_id_dirty_bit
>(
965 state
, dst_df
, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
966 eax_context_commit_property
<eax_distance_factor_dirty_bit
>(
967 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flDistanceFactor
);
968 eax_context_commit_property
<eax_air_absorption_hf_dirty_bit
>(
969 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF
);
970 eax_context_commit_property
<eax_hf_reference_dirty_bit
>(
971 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flHFReference
);
973 eax_df_
= EaxDirtyFlags
{};
976 void ALCcontext::eax5_context_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
)
978 if(eax_df_
== EaxDirtyFlags
{})
981 eax_context_commit_property
<eax_primary_fx_slot_id_dirty_bit
>(
982 state
, dst_df
, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
983 eax_context_commit_property
<eax_distance_factor_dirty_bit
>(
984 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flDistanceFactor
);
985 eax_context_commit_property
<eax_air_absorption_hf_dirty_bit
>(
986 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF
);
987 eax_context_commit_property
<eax_hf_reference_dirty_bit
>(
988 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flHFReference
);
989 eax_context_commit_property
<eax_macro_fx_factor_dirty_bit
>(
990 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flMacroFXFactor
);
992 eax_df_
= EaxDirtyFlags
{};
995 void ALCcontext::eax_context_commit()
997 auto dst_df
= EaxDirtyFlags
{};
1004 eax5_context_commit(eax123_
, dst_df
);
1007 eax4_context_commit(eax4_
, dst_df
);
1010 eax5_context_commit(eax5_
, dst_df
);
1013 eax_fail_unknown_version();
1016 if(dst_df
== EaxDirtyFlags
{})
1019 if((dst_df
& eax_primary_fx_slot_id_dirty_bit
) != EaxDirtyFlags
{})
1020 eax_context_commit_primary_fx_slot_id();
1022 if((dst_df
& eax_distance_factor_dirty_bit
) != EaxDirtyFlags
{})
1023 eax_context_commit_distance_factor();
1025 if((dst_df
& eax_air_absorption_hf_dirty_bit
) != EaxDirtyFlags
{})
1026 eax_context_commit_air_absorbtion_hf();
1028 if((dst_df
& eax_hf_reference_dirty_bit
) != EaxDirtyFlags
{})
1029 eax_context_commit_hf_reference();
1031 if((dst_df
& eax_macro_fx_factor_dirty_bit
) != EaxDirtyFlags
{})
1032 eax_context_commit_macro_fx_factor();
1034 if((dst_df
& eax_primary_fx_slot_id_dirty_bit
) != EaxDirtyFlags
{})
1035 eax_update_sources();
1038 void ALCcontext::eax_commit()
1040 eax_context_commit();
1045 class EaxSetException
: public EaxException
{
1047 explicit EaxSetException(const char* message
)
1048 : EaxException
{"EAX_SET", message
}
1052 [[noreturn
]] void eax_fail_set(const char* message
)
1054 throw EaxSetException
{message
};
1057 class EaxGetException
: public EaxException
{
1059 explicit EaxGetException(const char* message
)
1060 : EaxException
{"EAX_GET", message
}
1064 [[noreturn
]] void eax_fail_get(const char* message
)
1066 throw EaxGetException
{message
};
1072 FORCE_ALIGN ALenum AL_APIENTRY
EAXSet(
1073 const GUID
* property_set_id
,
1075 ALuint property_source_id
,
1076 ALvoid
* property_value
,
1077 ALuint property_value_size
) noexcept
1080 auto context
= GetContextRef();
1083 eax_fail_set("No current context.");
1085 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1087 return context
->eax_eax_set(
1092 property_value_size
);
1096 eax_log_exception(__func__
);
1097 return AL_INVALID_OPERATION
;
1100 FORCE_ALIGN ALenum AL_APIENTRY
EAXGet(
1101 const GUID
* property_set_id
,
1103 ALuint property_source_id
,
1104 ALvoid
* property_value
,
1105 ALuint property_value_size
) noexcept
1108 auto context
= GetContextRef();
1111 eax_fail_get("No current context.");
1113 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1115 return context
->eax_eax_get(
1120 property_value_size
);
1124 eax_log_exception(__func__
);
1125 return AL_INVALID_OPERATION
;
1127 #endif // ALSOFT_EAX