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
<ALCcontext
*> ALCcontext::sGlobalContext
{nullptr};
89 thread_local ALCcontext
*ALCcontext::sLocalContext
{nullptr};
90 ALCcontext::ThreadCtx::~ThreadCtx()
92 if(ALCcontext
*ctx
{ALCcontext::sLocalContext
})
94 const bool result
{ctx
->releaseIfNoDelete()};
95 ERR("Context %p current for thread being destroyed%s!\n", voidp
{ctx
},
96 result
? "" : ", leak detected");
99 thread_local
ALCcontext::ThreadCtx
ALCcontext::sThreadContext
;
101 ALeffect
ALCcontext::sDefaultEffect
;
105 ALCcontext
*ALCcontext::getThreadContext() noexcept
106 { return sLocalContext
; }
107 void ALCcontext::setThreadContext(ALCcontext
*context
) noexcept
108 { sThreadContext
.set(context
); }
111 ALCcontext::ALCcontext(al::intrusive_ptr
<ALCdevice
> device
)
112 : ContextBase
{device
.get()}, mALDevice
{std::move(device
)}
116 ALCcontext::~ALCcontext()
118 TRACE("Freeing context %p\n", voidp
{this});
120 size_t count
{std::accumulate(mSourceList
.cbegin(), mSourceList
.cend(), size_t{0u},
121 [](size_t cur
, const SourceSubList
&sublist
) noexcept
-> size_t
122 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); })};
124 WARN("%zu Source%s not deleted\n", count
, (count
==1)?"":"s");
132 mDefaultSlot
= nullptr;
133 count
= std::accumulate(mEffectSlotList
.cbegin(), mEffectSlotList
.cend(), size_t{0u},
134 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
135 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); });
137 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count
, (count
==1)?"":"s");
138 mEffectSlotList
.clear();
142 void ALCcontext::init()
144 if(sDefaultEffect
.type
!= AL_EFFECT_NULL
&& mDevice
->Type
== DeviceType::Playback
)
146 mDefaultSlot
= std::make_unique
<ALeffectslot
>(this);
147 aluInitEffectPanning(mDefaultSlot
->mSlot
, this);
150 EffectSlotArray
*auxslots
;
152 auxslots
= EffectSlot::CreatePtrArray(0);
155 auxslots
= EffectSlot::CreatePtrArray(1);
156 (*auxslots
)[0] = mDefaultSlot
->mSlot
;
157 mDefaultSlot
->mState
= SlotState::Playing
;
159 mActiveAuxSlots
.store(auxslots
, std::memory_order_relaxed
);
163 VoiceChange
*cur
{mVoiceChangeTail
};
164 while(VoiceChange
*next
{cur
->mNext
.load(std::memory_order_relaxed
)})
166 mCurrentVoiceChange
.store(cur
, std::memory_order_relaxed
);
169 mExtensionList
= alExtList
;
172 eax_initialize_extensions();
175 mParams
.Position
= alu::Vector
{0.0f
, 0.0f
, 0.0f
, 1.0f
};
176 mParams
.Matrix
= alu::Matrix::Identity();
177 mParams
.Velocity
= alu::Vector
{};
178 mParams
.Gain
= mListener
.Gain
;
179 mParams
.MetersPerUnit
= mListener
.mMetersPerUnit
;
180 mParams
.AirAbsorptionGainHF
= mAirAbsorptionGainHF
;
181 mParams
.DopplerFactor
= mDopplerFactor
;
182 mParams
.SpeedOfSound
= mSpeedOfSound
* mDopplerVelocity
;
183 mParams
.SourceDistanceModel
= mSourceDistanceModel
;
184 mParams
.mDistanceModel
= mDistanceModel
;
187 mAsyncEvents
= RingBuffer::Create(511, sizeof(AsyncEvent
), false);
188 StartEventThrd(this);
192 mActiveVoiceCount
.store(64, std::memory_order_relaxed
);
195 bool ALCcontext::deinit()
197 if(sLocalContext
== this)
199 WARN("%p released while current on thread\n", voidp
{this});
200 sThreadContext
.set(nullptr);
204 ALCcontext
*origctx
{this};
205 if(sGlobalContext
.compare_exchange_strong(origctx
, nullptr))
209 /* First make sure this context exists in the device's list. */
210 auto *oldarray
= mDevice
->mContexts
.load(std::memory_order_acquire
);
211 if(auto toremove
= static_cast<size_t>(std::count(oldarray
->begin(), oldarray
->end(), this)))
213 using ContextArray
= al::FlexArray
<ContextBase
*>;
214 auto alloc_ctx_array
= [](const size_t count
) -> ContextArray
*
216 if(count
== 0) return &DeviceBase::sEmptyContextArray
;
217 return ContextArray::Create(count
).release();
219 auto *newarray
= alloc_ctx_array(oldarray
->size() - toremove
);
221 /* Copy the current/old context handles to the new array, excluding the
224 std::copy_if(oldarray
->begin(), oldarray
->end(), newarray
->begin(),
225 std::bind(std::not_equal_to
<>{}, _1
, this));
227 /* Store the new context array in the device. Wait for any current mix
228 * to finish before deleting the old array.
230 mDevice
->mContexts
.store(newarray
);
231 if(oldarray
!= &DeviceBase::sEmptyContextArray
)
233 mDevice
->waitForMix();
237 ret
= !newarray
->empty();
240 ret
= !oldarray
->empty();
247 void ALCcontext::applyAllUpdates()
249 /* Tell the mixer to stop applying updates, then wait for any active
250 * updating to finish, before providing updates.
252 mHoldUpdates
.store(true, std::memory_order_release
);
253 while((mUpdateCount
.load(std::memory_order_acquire
)&1) != 0) {
258 if(eax_is_initialized_
)
261 if(std::exchange(mPropsDirty
, false))
262 UpdateContextProps(this);
263 UpdateAllEffectSlotProps(this);
264 UpdateAllSourceProps(this);
266 /* Now with all updates declared, let the mixer continue applying them so
267 * they all happen at once.
269 mHoldUpdates
.store(false, std::memory_order_release
);
276 void ForEachSource(ALCcontext
*context
, F func
)
278 for(auto &sublist
: context
->mSourceList
)
280 uint64_t usemask
{~sublist
.FreeMask
};
283 const int idx
{al::countr_zero(usemask
)};
284 usemask
&= ~(1_u64
<< idx
);
286 func(sublist
.Sources
[idx
]);
294 bool ALCcontext::eax_is_capable() const noexcept
296 return eax_has_enough_aux_sends();
299 void ALCcontext::eax_uninitialize() noexcept
301 if(!eax_is_initialized_
)
304 eax_is_initialized_
= false;
305 eax_is_tried_
= false;
306 eax_fx_slots_
.uninitialize();
309 ALenum
ALCcontext::eax_eax_set(
310 const GUID
* property_set_id
,
312 ALuint property_source_id
,
313 ALvoid
* property_value
,
314 ALuint property_value_size
)
316 const auto call
= create_eax_call(
322 property_value_size
);
324 const auto eax_version
= call
.get_version();
325 if(eax_version
!= eax_version_
)
326 eax_df_
= ~EaxDirtyFlags();
327 eax_version_
= eax_version
;
330 switch(call
.get_property_set_id())
332 case EaxCallPropertySetId::context
:
335 case EaxCallPropertySetId::fx_slot
:
336 case EaxCallPropertySetId::fx_slot_effect
:
337 eax_dispatch_fx_slot(call
);
339 case EaxCallPropertySetId::source
:
340 eax_dispatch_source(call
);
343 eax_fail_unknown_property_set_id();
346 if(!call
.is_deferred() && !mDeferUpdates
)
352 ALenum
ALCcontext::eax_eax_get(
353 const GUID
* property_set_id
,
355 ALuint property_source_id
,
356 ALvoid
* property_value
,
357 ALuint property_value_size
)
359 const auto call
= create_eax_call(
365 property_value_size
);
366 eax_version_
= call
.get_version();
369 switch(call
.get_property_set_id())
371 case EaxCallPropertySetId::context
:
374 case EaxCallPropertySetId::fx_slot
:
375 case EaxCallPropertySetId::fx_slot_effect
:
376 eax_dispatch_fx_slot(call
);
378 case EaxCallPropertySetId::source
:
379 eax_dispatch_source(call
);
382 eax_fail_unknown_property_set_id();
388 void ALCcontext::eax_commit_and_update_sources()
390 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
391 ForEachSource(this, std::mem_fn(&ALsource::eax_commit_and_update
));
394 void ALCcontext::eax_set_last_error() noexcept
396 eax_last_error_
= EAXERR_INVALID_OPERATION
;
399 [[noreturn
]] void ALCcontext::eax_fail(const char* message
)
401 throw ContextException
{message
};
404 [[noreturn
]] void ALCcontext::eax_fail_unknown_property_set_id()
406 eax_fail("Unknown property ID.");
409 [[noreturn
]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
411 eax_fail("Unknown primary FX Slot ID.");
414 [[noreturn
]] void ALCcontext::eax_fail_unknown_property_id()
416 eax_fail("Unknown property ID.");
419 [[noreturn
]] void ALCcontext::eax_fail_unknown_version()
421 eax_fail("Unknown version.");
424 void ALCcontext::eax_initialize_extensions()
426 if(!eax_g_is_enabled
)
429 const auto string_max_capacity
=
430 std::strlen(mExtensionList
) + 1 +
431 std::strlen(eax1_ext_name
) + 1 +
432 std::strlen(eax2_ext_name
) + 1 +
433 std::strlen(eax3_ext_name
) + 1 +
434 std::strlen(eax4_ext_name
) + 1 +
435 std::strlen(eax5_ext_name
) + 1 +
436 std::strlen(eax_x_ram_ext_name
) + 1;
438 eax_extension_list_
.reserve(string_max_capacity
);
440 if(eax_is_capable()) {
441 eax_extension_list_
+= eax1_ext_name
;
442 eax_extension_list_
+= ' ';
444 eax_extension_list_
+= eax2_ext_name
;
445 eax_extension_list_
+= ' ';
447 eax_extension_list_
+= eax3_ext_name
;
448 eax_extension_list_
+= ' ';
450 eax_extension_list_
+= eax4_ext_name
;
451 eax_extension_list_
+= ' ';
453 eax_extension_list_
+= eax5_ext_name
;
454 eax_extension_list_
+= ' ';
457 eax_extension_list_
+= eax_x_ram_ext_name
;
458 eax_extension_list_
+= ' ';
460 eax_extension_list_
+= mExtensionList
;
461 mExtensionList
= eax_extension_list_
.c_str();
464 void ALCcontext::eax_initialize()
466 if(eax_is_initialized_
)
472 eax_is_tried_
= true;
474 if(!eax_g_is_enabled
)
475 eax_fail("EAX disabled by a configuration.");
477 eax_ensure_compatibility();
479 eax_context_commit_air_absorbtion_hf();
480 eax_update_speaker_configuration();
481 eax_initialize_fx_slots();
482 eax_initialize_sources();
484 eax_is_initialized_
= true;
491 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
493 return mDefaultSlot
== nullptr;
496 void ALCcontext::eax_ensure_no_default_effect_slot() const
498 if(!eax_has_no_default_effect_slot())
499 eax_fail("There is a default effect slot in the context.");
502 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
504 return mALDevice
->NumAuxSends
>= EAX_MAX_FXSLOTS
;
507 void ALCcontext::eax_ensure_enough_aux_sends() const
509 if(!eax_has_enough_aux_sends())
510 eax_fail("Not enough aux sends.");
513 void ALCcontext::eax_ensure_compatibility()
515 eax_ensure_enough_aux_sends();
518 unsigned long ALCcontext::eax_detect_speaker_configuration() const
520 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
522 switch(mDevice
->FmtChans
)
524 case DevFmtMono
: return SPEAKERS_2
;
526 /* Pretend 7.1 if using UHJ output, since they both provide full
527 * horizontal surround.
529 if(mDevice
->mUhjEncoder
)
531 if(mDevice
->Flags
.test(DirectEar
))
534 case DevFmtQuad
: return SPEAKERS_4
;
535 case DevFmtX51
: return SPEAKERS_5
;
536 case DevFmtX61
: return SPEAKERS_6
;
537 case DevFmtX71
: return SPEAKERS_7
;
538 /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
539 * suggest with-height surround sound (like HRTF).
541 case DevFmtX714
: return SPEAKERS_7
;
542 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
543 * suggest full-sphere surround sound (like HRTF).
545 case DevFmtX3D71
: return SPEAKERS_5
;
546 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
547 * provide full-sphere surround sound. Depends if apps are more likely to
548 * consider headphones or 7.1 for surround sound support.
550 case DevFmtAmbi3D
: return SPEAKERS_7
;
552 ERR(EAX_PREFIX
"Unexpected device channel format 0x%x.\n", mDevice
->FmtChans
);
558 void ALCcontext::eax_update_speaker_configuration()
560 eax_speaker_config_
= eax_detect_speaker_configuration();
563 void ALCcontext::eax_set_last_error_defaults() noexcept
565 eax_last_error_
= EAX_OK
;
568 void ALCcontext::eax_session_set_defaults() noexcept
570 eax_session_
.ulEAXVersion
= EAXCONTEXT_DEFAULTEAXSESSION
;
571 eax_session_
.ulMaxActiveSends
= EAXCONTEXT_DEFAULTMAXACTIVESENDS
;
574 void ALCcontext::eax4_context_set_defaults(Eax4Props
& props
) noexcept
576 props
.guidPrimaryFXSlotID
= EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID
;
577 props
.flDistanceFactor
= EAXCONTEXT_DEFAULTDISTANCEFACTOR
;
578 props
.flAirAbsorptionHF
= EAXCONTEXT_DEFAULTAIRABSORPTIONHF
;
579 props
.flHFReference
= EAXCONTEXT_DEFAULTHFREFERENCE
;
582 void ALCcontext::eax4_context_set_defaults(Eax4State
& state
) noexcept
584 eax4_context_set_defaults(state
.i
);
588 void ALCcontext::eax5_context_set_defaults(Eax5Props
& props
) noexcept
590 props
.guidPrimaryFXSlotID
= EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID
;
591 props
.flDistanceFactor
= EAXCONTEXT_DEFAULTDISTANCEFACTOR
;
592 props
.flAirAbsorptionHF
= EAXCONTEXT_DEFAULTAIRABSORPTIONHF
;
593 props
.flHFReference
= EAXCONTEXT_DEFAULTHFREFERENCE
;
594 props
.flMacroFXFactor
= EAXCONTEXT_DEFAULTMACROFXFACTOR
;
597 void ALCcontext::eax5_context_set_defaults(Eax5State
& state
) noexcept
599 eax5_context_set_defaults(state
.i
);
603 void ALCcontext::eax4_context_set_current_defaults(const Eax4Props
& props
) noexcept
605 static_cast<Eax4Props
&>(eax_
) = props
;
606 eax_
.flMacroFXFactor
= EAXCONTEXT_DEFAULTMACROFXFACTOR
;
609 void ALCcontext::eax5_context_set_current_defaults(const Eax5Props
& props
) noexcept
614 void ALCcontext::eax_context_set_current_defaults()
621 eax5_context_set_current_defaults(eax123_
.i
);
624 eax4_context_set_current_defaults(eax4_
.i
);
627 eax5_context_set_current_defaults(eax5_
.i
);
630 eax_fail_unknown_version();
633 eax_df_
= ~EaxDirtyFlags
{};
636 void ALCcontext::eax_context_set_defaults()
638 eax5_context_set_defaults(eax123_
);
639 eax4_context_set_defaults(eax4_
);
640 eax5_context_set_defaults(eax5_
);
641 eax_context_set_current_defaults();
644 void ALCcontext::eax_set_defaults()
646 eax_set_last_error_defaults();
647 eax_session_set_defaults();
648 eax_context_set_defaults();
651 void ALCcontext::eax_dispatch_fx_slot(const EaxCall
& call
)
653 const auto fx_slot_index
= call
.get_fx_slot_index();
654 if(!fx_slot_index
.has_value())
655 eax_fail("Invalid fx slot index.");
657 auto& fx_slot
= eax_get_fx_slot(*fx_slot_index
);
658 if(fx_slot
.eax_dispatch(call
))
660 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
661 ForEachSource(this, [](ALsource
& source
){ source
.eax_mark_as_changed(); });
665 void ALCcontext::eax_dispatch_source(const EaxCall
& call
)
667 const auto source_id
= call
.get_property_al_name();
668 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
669 const auto source
= ALsource::eax_lookup_source(*this, source_id
);
671 if (source
== nullptr)
672 eax_fail("Source not found.");
674 source
->eax_dispatch(call
);
677 void ALCcontext::eax_get_misc(const EaxCall
& call
)
679 switch(call
.get_property_id())
681 case EAXCONTEXT_NONE
:
683 case EAXCONTEXT_LASTERROR
:
684 call
.set_value
<ContextException
>(eax_last_error_
);
686 case EAXCONTEXT_SPEAKERCONFIG
:
687 call
.set_value
<ContextException
>(eax_speaker_config_
);
689 case EAXCONTEXT_EAXSESSION
:
690 call
.set_value
<ContextException
>(eax_session_
);
693 eax_fail_unknown_property_id();
697 void ALCcontext::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
699 switch(call
.get_property_id())
701 case EAXCONTEXT_ALLPARAMETERS
:
702 call
.set_value
<ContextException
>(props
);
704 case EAXCONTEXT_PRIMARYFXSLOTID
:
705 call
.set_value
<ContextException
>(props
.guidPrimaryFXSlotID
);
707 case EAXCONTEXT_DISTANCEFACTOR
:
708 call
.set_value
<ContextException
>(props
.flDistanceFactor
);
710 case EAXCONTEXT_AIRABSORPTIONHF
:
711 call
.set_value
<ContextException
>(props
.flAirAbsorptionHF
);
713 case EAXCONTEXT_HFREFERENCE
:
714 call
.set_value
<ContextException
>(props
.flHFReference
);
722 void ALCcontext::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
724 switch(call
.get_property_id())
726 case EAXCONTEXT_ALLPARAMETERS
:
727 call
.set_value
<ContextException
>(props
);
729 case EAXCONTEXT_PRIMARYFXSLOTID
:
730 call
.set_value
<ContextException
>(props
.guidPrimaryFXSlotID
);
732 case EAXCONTEXT_DISTANCEFACTOR
:
733 call
.set_value
<ContextException
>(props
.flDistanceFactor
);
735 case EAXCONTEXT_AIRABSORPTIONHF
:
736 call
.set_value
<ContextException
>(props
.flAirAbsorptionHF
);
738 case EAXCONTEXT_HFREFERENCE
:
739 call
.set_value
<ContextException
>(props
.flHFReference
);
741 case EAXCONTEXT_MACROFXFACTOR
:
742 call
.set_value
<ContextException
>(props
.flMacroFXFactor
);
750 void ALCcontext::eax_get(const EaxCall
& call
)
752 switch(call
.get_version())
754 case 4: eax4_get(call
, eax4_
.i
); break;
755 case 5: eax5_get(call
, eax5_
.i
); break;
756 default: eax_fail_unknown_version();
760 void ALCcontext::eax_context_commit_primary_fx_slot_id()
762 eax_primary_fx_slot_index_
= eax_
.guidPrimaryFXSlotID
;
765 void ALCcontext::eax_context_commit_distance_factor()
767 if(mListener
.mMetersPerUnit
== eax_
.flDistanceFactor
)
770 mListener
.mMetersPerUnit
= eax_
.flDistanceFactor
;
774 void ALCcontext::eax_context_commit_air_absorbtion_hf()
776 const auto new_value
= level_mb_to_gain(eax_
.flAirAbsorptionHF
);
778 if(mAirAbsorptionGainHF
== new_value
)
781 mAirAbsorptionGainHF
= new_value
;
785 void ALCcontext::eax_context_commit_hf_reference()
790 void ALCcontext::eax_context_commit_macro_fx_factor()
795 void ALCcontext::eax_initialize_fx_slots()
797 eax_fx_slots_
.initialize(*this);
798 eax_primary_fx_slot_index_
= eax_
.guidPrimaryFXSlotID
;
801 void ALCcontext::eax_initialize_sources()
803 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
804 auto init_source
= [this](ALsource
&source
) noexcept
805 { source
.eax_initialize(this); };
806 ForEachSource(this, init_source
);
809 void ALCcontext::eax_update_sources()
811 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
812 auto update_source
= [](ALsource
&source
)
813 { source
.eax_commit(); };
814 ForEachSource(this, update_source
);
817 void ALCcontext::eax_set_misc(const EaxCall
& call
)
819 switch(call
.get_property_id())
821 case EAXCONTEXT_NONE
:
823 case EAXCONTEXT_SPEAKERCONFIG
:
824 eax_set
<Eax5SpeakerConfigValidator
>(call
, eax_speaker_config_
);
826 case EAXCONTEXT_EAXSESSION
:
827 eax_set
<Eax5SessionAllValidator
>(call
, eax_session_
);
830 eax_fail_unknown_property_id();
834 void ALCcontext::eax4_defer_all(const EaxCall
& call
, Eax4State
& state
)
836 const auto& src
= call
.get_value
<ContextException
, const EAX40CONTEXTPROPERTIES
>();
837 Eax4AllValidator
{}(src
);
838 const auto& dst_i
= state
.i
;
839 auto& dst_d
= state
.d
;
842 if(dst_i
.guidPrimaryFXSlotID
!= dst_d
.guidPrimaryFXSlotID
)
843 eax_df_
|= eax_primary_fx_slot_id_dirty_bit
;
845 if(dst_i
.flDistanceFactor
!= dst_d
.flDistanceFactor
)
846 eax_df_
|= eax_distance_factor_dirty_bit
;
848 if(dst_i
.flAirAbsorptionHF
!= dst_d
.flAirAbsorptionHF
)
849 eax_df_
|= eax_air_absorption_hf_dirty_bit
;
851 if(dst_i
.flHFReference
!= dst_d
.flHFReference
)
852 eax_df_
|= eax_hf_reference_dirty_bit
;
855 void ALCcontext::eax4_defer(const EaxCall
& call
, Eax4State
& state
)
857 switch(call
.get_property_id())
859 case EAXCONTEXT_ALLPARAMETERS
:
860 eax4_defer_all(call
, state
);
862 case EAXCONTEXT_PRIMARYFXSLOTID
:
863 eax_defer
<Eax4PrimaryFxSlotIdValidator
, eax_primary_fx_slot_id_dirty_bit
>(
864 call
, state
, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
866 case EAXCONTEXT_DISTANCEFACTOR
:
867 eax_defer
<Eax4DistanceFactorValidator
, eax_distance_factor_dirty_bit
>(
868 call
, state
, &EAX40CONTEXTPROPERTIES::flDistanceFactor
);
870 case EAXCONTEXT_AIRABSORPTIONHF
:
871 eax_defer
<Eax4AirAbsorptionHfValidator
, eax_air_absorption_hf_dirty_bit
>(
872 call
, state
, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF
);
874 case EAXCONTEXT_HFREFERENCE
:
875 eax_defer
<Eax4HfReferenceValidator
, eax_hf_reference_dirty_bit
>(
876 call
, state
, &EAX40CONTEXTPROPERTIES::flHFReference
);
884 void ALCcontext::eax5_defer_all(const EaxCall
& call
, Eax5State
& state
)
886 const auto& src
= call
.get_value
<ContextException
, const EAX50CONTEXTPROPERTIES
>();
887 Eax4AllValidator
{}(src
);
888 const auto& dst_i
= state
.i
;
889 auto& dst_d
= state
.d
;
892 if(dst_i
.guidPrimaryFXSlotID
!= dst_d
.guidPrimaryFXSlotID
)
893 eax_df_
|= eax_primary_fx_slot_id_dirty_bit
;
895 if(dst_i
.flDistanceFactor
!= dst_d
.flDistanceFactor
)
896 eax_df_
|= eax_distance_factor_dirty_bit
;
898 if(dst_i
.flAirAbsorptionHF
!= dst_d
.flAirAbsorptionHF
)
899 eax_df_
|= eax_air_absorption_hf_dirty_bit
;
901 if(dst_i
.flHFReference
!= dst_d
.flHFReference
)
902 eax_df_
|= eax_hf_reference_dirty_bit
;
904 if(dst_i
.flMacroFXFactor
!= dst_d
.flMacroFXFactor
)
905 eax_df_
|= eax_macro_fx_factor_dirty_bit
;
908 void ALCcontext::eax5_defer(const EaxCall
& call
, Eax5State
& state
)
910 switch(call
.get_property_id())
912 case EAXCONTEXT_ALLPARAMETERS
:
913 eax5_defer_all(call
, state
);
915 case EAXCONTEXT_PRIMARYFXSLOTID
:
916 eax_defer
<Eax5PrimaryFxSlotIdValidator
, eax_primary_fx_slot_id_dirty_bit
>(
917 call
, state
, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
919 case EAXCONTEXT_DISTANCEFACTOR
:
920 eax_defer
<Eax4DistanceFactorValidator
, eax_distance_factor_dirty_bit
>(
921 call
, state
, &EAX50CONTEXTPROPERTIES::flDistanceFactor
);
923 case EAXCONTEXT_AIRABSORPTIONHF
:
924 eax_defer
<Eax4AirAbsorptionHfValidator
, eax_air_absorption_hf_dirty_bit
>(
925 call
, state
, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF
);
927 case EAXCONTEXT_HFREFERENCE
:
928 eax_defer
<Eax4HfReferenceValidator
, eax_hf_reference_dirty_bit
>(
929 call
, state
, &EAX50CONTEXTPROPERTIES::flHFReference
);
931 case EAXCONTEXT_MACROFXFACTOR
:
932 eax_defer
<Eax5MacroFxFactorValidator
, eax_macro_fx_factor_dirty_bit
>(
933 call
, state
, &EAX50CONTEXTPROPERTIES::flMacroFXFactor
);
941 void ALCcontext::eax_set(const EaxCall
& call
)
943 switch(call
.get_version())
945 case 4: eax4_defer(call
, eax4_
); break;
946 case 5: eax5_defer(call
, eax5_
); break;
947 default: eax_fail_unknown_version();
951 void ALCcontext::eax4_context_commit(Eax4State
& state
, EaxDirtyFlags
& dst_df
)
953 if(eax_df_
== EaxDirtyFlags
{})
956 eax_context_commit_property
<eax_primary_fx_slot_id_dirty_bit
>(
957 state
, dst_df
, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
958 eax_context_commit_property
<eax_distance_factor_dirty_bit
>(
959 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flDistanceFactor
);
960 eax_context_commit_property
<eax_air_absorption_hf_dirty_bit
>(
961 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF
);
962 eax_context_commit_property
<eax_hf_reference_dirty_bit
>(
963 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flHFReference
);
965 eax_df_
= EaxDirtyFlags
{};
968 void ALCcontext::eax5_context_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
)
970 if(eax_df_
== EaxDirtyFlags
{})
973 eax_context_commit_property
<eax_primary_fx_slot_id_dirty_bit
>(
974 state
, dst_df
, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
975 eax_context_commit_property
<eax_distance_factor_dirty_bit
>(
976 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flDistanceFactor
);
977 eax_context_commit_property
<eax_air_absorption_hf_dirty_bit
>(
978 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF
);
979 eax_context_commit_property
<eax_hf_reference_dirty_bit
>(
980 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flHFReference
);
981 eax_context_commit_property
<eax_macro_fx_factor_dirty_bit
>(
982 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flMacroFXFactor
);
984 eax_df_
= EaxDirtyFlags
{};
987 void ALCcontext::eax_context_commit()
989 auto dst_df
= EaxDirtyFlags
{};
996 eax5_context_commit(eax123_
, dst_df
);
999 eax4_context_commit(eax4_
, dst_df
);
1002 eax5_context_commit(eax5_
, dst_df
);
1005 eax_fail_unknown_version();
1008 if(dst_df
== EaxDirtyFlags
{})
1011 if((dst_df
& eax_primary_fx_slot_id_dirty_bit
) != EaxDirtyFlags
{})
1012 eax_context_commit_primary_fx_slot_id();
1014 if((dst_df
& eax_distance_factor_dirty_bit
) != EaxDirtyFlags
{})
1015 eax_context_commit_distance_factor();
1017 if((dst_df
& eax_air_absorption_hf_dirty_bit
) != EaxDirtyFlags
{})
1018 eax_context_commit_air_absorbtion_hf();
1020 if((dst_df
& eax_hf_reference_dirty_bit
) != EaxDirtyFlags
{})
1021 eax_context_commit_hf_reference();
1023 if((dst_df
& eax_macro_fx_factor_dirty_bit
) != EaxDirtyFlags
{})
1024 eax_context_commit_macro_fx_factor();
1026 if((dst_df
& eax_primary_fx_slot_id_dirty_bit
) != EaxDirtyFlags
{})
1027 eax_update_sources();
1030 void ALCcontext::eax_commit()
1032 eax_context_commit();
1037 class EaxSetException
: public EaxException
{
1039 explicit EaxSetException(const char* message
)
1040 : EaxException
{"EAX_SET", message
}
1044 [[noreturn
]] void eax_fail_set(const char* message
)
1046 throw EaxSetException
{message
};
1049 class EaxGetException
: public EaxException
{
1051 explicit EaxGetException(const char* message
)
1052 : EaxException
{"EAX_GET", message
}
1056 [[noreturn
]] void eax_fail_get(const char* message
)
1058 throw EaxGetException
{message
};
1064 FORCE_ALIGN ALenum AL_APIENTRY
EAXSet(
1065 const GUID
* property_set_id
,
1067 ALuint property_source_id
,
1068 ALvoid
* property_value
,
1069 ALuint property_value_size
) noexcept
1072 auto context
= GetContextRef();
1075 eax_fail_set("No current context.");
1077 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1079 return context
->eax_eax_set(
1084 property_value_size
);
1088 eax_log_exception(__func__
);
1089 return AL_INVALID_OPERATION
;
1092 FORCE_ALIGN ALenum AL_APIENTRY
EAXGet(
1093 const GUID
* property_set_id
,
1095 ALuint property_source_id
,
1096 ALvoid
* property_value
,
1097 ALuint property_value_size
) noexcept
1100 auto context
= GetContextRef();
1103 eax_fail_get("No current context.");
1105 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1107 return context
->eax_eax_get(
1112 property_value_size
);
1116 eax_log_exception(__func__
);
1117 return AL_INVALID_OPERATION
;
1119 #endif // ALSOFT_EAX