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"
37 #include "al/eax/exception.h"
38 #include "al/eax/globals.h"
43 using namespace std::placeholders
;
47 /* Default context extensions */
48 constexpr ALchar alExtList
[] =
52 "AL_EXT_EXPONENT_DISTANCE "
55 "AL_EXT_LINEAR_DISTANCE "
58 "AL_EXT_MULAW_BFORMAT "
59 "AL_EXT_MULAW_MCFORMATS "
61 "AL_EXT_source_distance_model "
62 "AL_EXT_SOURCE_RADIUS "
63 "AL_EXT_STEREO_ANGLES "
64 "AL_LOKI_quadriphonic "
66 "AL_SOFTX_bformat_hoa "
67 "AL_SOFT_block_alignment "
68 "AL_SOFT_callback_buffer "
69 "AL_SOFTX_convolution_reverb "
70 "AL_SOFT_deferred_updates "
71 "AL_SOFT_direct_channels "
72 "AL_SOFT_direct_channels_remix "
73 "AL_SOFT_effect_target "
75 "AL_SOFT_gain_clamp_ex "
76 "AL_SOFTX_hold_on_disconnect "
77 "AL_SOFT_loop_points "
78 "AL_SOFTX_map_buffer "
80 "AL_SOFT_source_latency "
81 "AL_SOFT_source_length "
82 "AL_SOFT_source_resampler "
83 "AL_SOFT_source_spatialize "
89 std::atomic
<ALCcontext
*> ALCcontext::sGlobalContext
{nullptr};
91 thread_local ALCcontext
*ALCcontext::sLocalContext
{nullptr};
92 ALCcontext::ThreadCtx::~ThreadCtx()
94 if(ALCcontext
*ctx
{ALCcontext::sLocalContext
})
96 const bool result
{ctx
->releaseIfNoDelete()};
97 ERR("Context %p current for thread being destroyed%s!\n", voidp
{ctx
},
98 result
? "" : ", leak detected");
101 thread_local
ALCcontext::ThreadCtx
ALCcontext::sThreadContext
;
103 ALeffect
ALCcontext::sDefaultEffect
;
107 ALCcontext
*ALCcontext::getThreadContext() noexcept
108 { return sLocalContext
; }
109 void ALCcontext::setThreadContext(ALCcontext
*context
) noexcept
110 { sThreadContext
.set(context
); }
113 ALCcontext::ALCcontext(al::intrusive_ptr
<ALCdevice
> device
)
114 : ContextBase
{device
.get()}, mALDevice
{std::move(device
)}
118 ALCcontext::~ALCcontext()
120 TRACE("Freeing context %p\n", voidp
{this});
122 size_t count
{std::accumulate(mSourceList
.cbegin(), mSourceList
.cend(), size_t{0u},
123 [](size_t cur
, const SourceSubList
&sublist
) noexcept
-> size_t
124 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); })};
126 WARN("%zu Source%s not deleted\n", count
, (count
==1)?"":"s");
134 mDefaultSlot
= nullptr;
135 count
= std::accumulate(mEffectSlotList
.cbegin(), mEffectSlotList
.cend(), size_t{0u},
136 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
137 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); });
139 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count
, (count
==1)?"":"s");
140 mEffectSlotList
.clear();
144 void ALCcontext::init()
146 if(sDefaultEffect
.type
!= AL_EFFECT_NULL
&& mDevice
->Type
== DeviceType::Playback
)
148 mDefaultSlot
= std::make_unique
<ALeffectslot
>();
149 aluInitEffectPanning(&mDefaultSlot
->mSlot
, this);
152 EffectSlotArray
*auxslots
;
154 auxslots
= EffectSlot::CreatePtrArray(0);
157 auxslots
= EffectSlot::CreatePtrArray(1);
158 (*auxslots
)[0] = &mDefaultSlot
->mSlot
;
159 mDefaultSlot
->mState
= SlotState::Playing
;
161 mActiveAuxSlots
.store(auxslots
, std::memory_order_relaxed
);
165 VoiceChange
*cur
{mVoiceChangeTail
};
166 while(VoiceChange
*next
{cur
->mNext
.load(std::memory_order_relaxed
)})
168 mCurrentVoiceChange
.store(cur
, std::memory_order_relaxed
);
171 mExtensionList
= alExtList
;
174 eax_initialize_extensions();
177 mParams
.Position
= alu::Vector
{0.0f
, 0.0f
, 0.0f
, 1.0f
};
178 mParams
.Matrix
= alu::Matrix::Identity();
179 mParams
.Velocity
= alu::Vector
{};
180 mParams
.Gain
= mListener
.Gain
;
181 mParams
.MetersPerUnit
= mListener
.mMetersPerUnit
;
182 mParams
.AirAbsorptionGainHF
= mAirAbsorptionGainHF
;
183 mParams
.DopplerFactor
= mDopplerFactor
;
184 mParams
.SpeedOfSound
= mSpeedOfSound
* mDopplerVelocity
;
185 mParams
.SourceDistanceModel
= mSourceDistanceModel
;
186 mParams
.mDistanceModel
= mDistanceModel
;
189 mAsyncEvents
= RingBuffer::Create(511, sizeof(AsyncEvent
), false);
190 StartEventThrd(this);
194 mActiveVoiceCount
.store(64, std::memory_order_relaxed
);
197 bool ALCcontext::deinit()
199 if(sLocalContext
== this)
201 WARN("%p released while current on thread\n", voidp
{this});
202 sThreadContext
.set(nullptr);
206 ALCcontext
*origctx
{this};
207 if(sGlobalContext
.compare_exchange_strong(origctx
, nullptr))
211 /* First make sure this context exists in the device's list. */
212 auto *oldarray
= mDevice
->mContexts
.load(std::memory_order_acquire
);
213 if(auto toremove
= static_cast<size_t>(std::count(oldarray
->begin(), oldarray
->end(), this)))
215 using ContextArray
= al::FlexArray
<ContextBase
*>;
216 auto alloc_ctx_array
= [](const size_t count
) -> ContextArray
*
218 if(count
== 0) return &DeviceBase::sEmptyContextArray
;
219 return ContextArray::Create(count
).release();
221 auto *newarray
= alloc_ctx_array(oldarray
->size() - toremove
);
223 /* Copy the current/old context handles to the new array, excluding the
226 std::copy_if(oldarray
->begin(), oldarray
->end(), newarray
->begin(),
227 std::bind(std::not_equal_to
<>{}, _1
, this));
229 /* Store the new context array in the device. Wait for any current mix
230 * to finish before deleting the old array.
232 mDevice
->mContexts
.store(newarray
);
233 if(oldarray
!= &DeviceBase::sEmptyContextArray
)
235 mDevice
->waitForMix();
239 ret
= !newarray
->empty();
242 ret
= !oldarray
->empty();
249 void ALCcontext::applyAllUpdates()
251 /* Tell the mixer to stop applying updates, then wait for any active
252 * updating to finish, before providing updates.
254 mHoldUpdates
.store(true, std::memory_order_release
);
255 while((mUpdateCount
.load(std::memory_order_acquire
)&1) != 0) {
260 eax_apply_deferred();
262 if(std::exchange(mPropsDirty
, false))
263 UpdateContextProps(this);
264 UpdateAllEffectSlotProps(this);
265 UpdateAllSourceProps(this);
267 /* Now with all updates declared, let the mixer continue applying them so
268 * they all happen at once.
270 mHoldUpdates
.store(false, std::memory_order_release
);
276 class ContextException
:
280 explicit ContextException(
283 EaxException
{"EAX_CONTEXT", message
}
286 }; // ContextException
290 void ForEachSource(ALCcontext
*context
, F func
)
292 for(auto &sublist
: context
->mSourceList
)
294 uint64_t usemask
{~sublist
.FreeMask
};
297 const int idx
{al::countr_zero(usemask
)};
298 usemask
&= ~(1_u64
<< idx
);
300 func(sublist
.Sources
[idx
]);
308 bool ALCcontext::eax_is_capable() const noexcept
310 return eax_has_enough_aux_sends();
313 void ALCcontext::eax_uninitialize() noexcept
315 if (!eax_is_initialized_
)
320 eax_is_initialized_
= true;
321 eax_is_tried_
= false;
323 eax_fx_slots_
.uninitialize();
326 ALenum
ALCcontext::eax_eax_set(
327 const GUID
* property_set_id
,
329 ALuint property_source_id
,
330 ALvoid
* property_value
,
331 ALuint property_value_size
)
335 const auto eax_call
= create_eax_call(
344 eax_unlock_legacy_fx_slots(eax_call
);
346 switch (eax_call
.get_property_set_id())
348 case EaxEaxCallPropertySetId::context
:
352 case EaxEaxCallPropertySetId::fx_slot
:
353 case EaxEaxCallPropertySetId::fx_slot_effect
:
354 eax_dispatch_fx_slot(eax_call
);
357 case EaxEaxCallPropertySetId::source
:
358 eax_dispatch_source(eax_call
);
362 eax_fail("Unsupported property set id.");
365 static constexpr auto deferred_flag
= 0x80000000u
;
366 if(!(property_id
&deferred_flag
) && !mDeferUpdates
)
372 ALenum
ALCcontext::eax_eax_get(
373 const GUID
* property_set_id
,
375 ALuint property_source_id
,
376 ALvoid
* property_value
,
377 ALuint property_value_size
)
381 const auto eax_call
= create_eax_call(
390 eax_unlock_legacy_fx_slots(eax_call
);
392 switch (eax_call
.get_property_set_id())
394 case EaxEaxCallPropertySetId::context
:
398 case EaxEaxCallPropertySetId::fx_slot
:
399 case EaxEaxCallPropertySetId::fx_slot_effect
:
400 eax_dispatch_fx_slot(eax_call
);
403 case EaxEaxCallPropertySetId::source
:
404 eax_dispatch_source(eax_call
);
408 eax_fail("Unsupported property set id.");
414 void ALCcontext::eax_update_filters()
416 ForEachSource(this, std::mem_fn(&ALsource::eax_update_filters
));
419 void ALCcontext::eax_commit_and_update_sources()
421 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
422 ForEachSource(this, std::mem_fn(&ALsource::eax_commit_and_update
));
425 void ALCcontext::eax_set_last_error() noexcept
427 eax_last_error_
= EAXERR_INVALID_OPERATION
;
431 void ALCcontext::eax_fail(
434 throw ContextException
{message
};
437 void ALCcontext::eax_initialize_extensions()
439 if (!eax_g_is_enabled
)
444 const auto string_max_capacity
=
445 std::strlen(mExtensionList
) + 1 +
446 std::strlen(eax1_ext_name
) + 1 +
447 std::strlen(eax2_ext_name
) + 1 +
448 std::strlen(eax3_ext_name
) + 1 +
449 std::strlen(eax4_ext_name
) + 1 +
450 std::strlen(eax5_ext_name
) + 1 +
451 std::strlen(eax_x_ram_ext_name
) + 1 +
454 eax_extension_list_
.reserve(string_max_capacity
);
456 if (eax_is_capable())
458 eax_extension_list_
+= eax1_ext_name
;
459 eax_extension_list_
+= ' ';
461 eax_extension_list_
+= eax2_ext_name
;
462 eax_extension_list_
+= ' ';
464 eax_extension_list_
+= eax3_ext_name
;
465 eax_extension_list_
+= ' ';
467 eax_extension_list_
+= eax4_ext_name
;
468 eax_extension_list_
+= ' ';
470 eax_extension_list_
+= eax5_ext_name
;
471 eax_extension_list_
+= ' ';
474 eax_extension_list_
+= eax_x_ram_ext_name
;
475 eax_extension_list_
+= ' ';
477 eax_extension_list_
+= mExtensionList
;
478 mExtensionList
= eax_extension_list_
.c_str();
481 void ALCcontext::eax_initialize()
483 if (eax_is_initialized_
)
493 eax_is_tried_
= true;
495 if (!eax_g_is_enabled
)
497 eax_fail("EAX disabled by a configuration.");
500 eax_ensure_compatibility();
502 eax_set_air_absorbtion_hf();
503 eax_update_speaker_configuration();
504 eax_initialize_fx_slots();
505 eax_initialize_sources();
507 eax_is_initialized_
= true;
510 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
512 return mDefaultSlot
== nullptr;
515 void ALCcontext::eax_ensure_no_default_effect_slot() const
517 if (!eax_has_no_default_effect_slot())
519 eax_fail("There is a default effect slot in the context.");
523 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
525 return mALDevice
->NumAuxSends
>= EAX_MAX_FXSLOTS
;
528 void ALCcontext::eax_ensure_enough_aux_sends() const
530 if (!eax_has_enough_aux_sends())
532 eax_fail("Not enough aux sends.");
536 void ALCcontext::eax_ensure_compatibility()
538 eax_ensure_enough_aux_sends();
541 unsigned long ALCcontext::eax_detect_speaker_configuration() const
543 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
545 switch(mDevice
->FmtChans
)
547 case DevFmtMono
: return SPEAKERS_2
;
549 /* Pretend 7.1 if using UHJ output, since they both provide full
550 * horizontal surround.
552 if(mDevice
->mUhjEncoder
)
554 if(mDevice
->Flags
.test(DirectEar
))
557 case DevFmtQuad
: return SPEAKERS_4
;
558 case DevFmtX51
: return SPEAKERS_5
;
559 case DevFmtX61
: return SPEAKERS_6
;
560 case DevFmtX71
: return SPEAKERS_7
;
561 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
562 * suggest full-sphere surround sound (like HRTF).
564 case DevFmtX3D71
: return SPEAKERS_5
;
565 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
566 * provide full-sphere surround sound. Depends if apps are more likely to
567 * consider headphones or 7.1 for surround sound support.
569 case DevFmtAmbi3D
: return SPEAKERS_7
;
571 ERR(EAX_PREFIX
"Unexpected device channel format 0x%x.\n", mDevice
->FmtChans
);
577 void ALCcontext::eax_update_speaker_configuration()
579 eax_speaker_config_
= eax_detect_speaker_configuration();
582 void ALCcontext::eax_set_last_error_defaults() noexcept
584 eax_last_error_
= EAX_OK
;
587 void ALCcontext::eax_set_session_defaults() noexcept
589 eax_session_
.ulEAXVersion
= EAXCONTEXT_MINEAXSESSION
;
590 eax_session_
.ulMaxActiveSends
= EAXCONTEXT_DEFAULTMAXACTIVESENDS
;
593 void ALCcontext::eax_set_context_defaults() noexcept
595 eax_
.context
.guidPrimaryFXSlotID
= EAXCONTEXT_DEFAULTPRIMARYFXSLOTID
;
596 eax_
.context
.flDistanceFactor
= EAXCONTEXT_DEFAULTDISTANCEFACTOR
;
597 eax_
.context
.flAirAbsorptionHF
= EAXCONTEXT_DEFAULTAIRABSORPTIONHF
;
598 eax_
.context
.flHFReference
= EAXCONTEXT_DEFAULTHFREFERENCE
;
601 void ALCcontext::eax_set_defaults() noexcept
603 eax_set_last_error_defaults();
604 eax_set_session_defaults();
605 eax_set_context_defaults();
610 void ALCcontext::eax_unlock_legacy_fx_slots(const EaxEaxCall
& eax_call
) noexcept
612 if (eax_call
.get_version() != 5 || eax_are_legacy_fx_slots_unlocked_
)
615 eax_are_legacy_fx_slots_unlocked_
= true;
616 eax_fx_slots_
.unlock_legacy();
619 void ALCcontext::eax_dispatch_fx_slot(
620 const EaxEaxCall
& eax_call
)
622 const auto fx_slot_index
= eax_call
.get_fx_slot_index();
623 if(!fx_slot_index
.has_value())
624 eax_fail("Invalid fx slot index.");
626 auto& fx_slot
= eax_get_fx_slot(*fx_slot_index
);
627 if(fx_slot
.eax_dispatch(eax_call
))
629 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
630 eax_update_filters();
634 void ALCcontext::eax_dispatch_source(
635 const EaxEaxCall
& eax_call
)
637 const auto source_id
= eax_call
.get_property_al_name();
639 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
641 const auto source
= ALsource::eax_lookup_source(*this, source_id
);
645 eax_fail("Source not found.");
648 source
->eax_dispatch(eax_call
);
651 void ALCcontext::eax_get_primary_fx_slot_id(
652 const EaxEaxCall
& eax_call
)
654 eax_call
.set_value
<ContextException
>(eax_
.context
.guidPrimaryFXSlotID
);
657 void ALCcontext::eax_get_distance_factor(
658 const EaxEaxCall
& eax_call
)
660 eax_call
.set_value
<ContextException
>(eax_
.context
.flDistanceFactor
);
663 void ALCcontext::eax_get_air_absorption_hf(
664 const EaxEaxCall
& eax_call
)
666 eax_call
.set_value
<ContextException
>(eax_
.context
.flAirAbsorptionHF
);
669 void ALCcontext::eax_get_hf_reference(
670 const EaxEaxCall
& eax_call
)
672 eax_call
.set_value
<ContextException
>(eax_
.context
.flHFReference
);
675 void ALCcontext::eax_get_last_error(
676 const EaxEaxCall
& eax_call
)
678 const auto eax_last_error
= eax_last_error_
;
679 eax_last_error_
= EAX_OK
;
680 eax_call
.set_value
<ContextException
>(eax_last_error
);
683 void ALCcontext::eax_get_speaker_config(
684 const EaxEaxCall
& eax_call
)
686 eax_call
.set_value
<ContextException
>(eax_speaker_config_
);
689 void ALCcontext::eax_get_session(
690 const EaxEaxCall
& eax_call
)
692 eax_call
.set_value
<ContextException
>(eax_session_
);
695 void ALCcontext::eax_get_macro_fx_factor(
696 const EaxEaxCall
& eax_call
)
698 eax_call
.set_value
<ContextException
>(eax_
.context
.flMacroFXFactor
);
701 void ALCcontext::eax_get_context_all(
702 const EaxEaxCall
& eax_call
)
704 switch (eax_call
.get_version())
707 eax_call
.set_value
<ContextException
>(static_cast<const EAX40CONTEXTPROPERTIES
&>(eax_
.context
));
711 eax_call
.set_value
<ContextException
>(static_cast<const EAX50CONTEXTPROPERTIES
&>(eax_
.context
));
715 eax_fail("Unsupported EAX version.");
719 void ALCcontext::eax_get(
720 const EaxEaxCall
& eax_call
)
722 switch (eax_call
.get_property_id())
724 case EAXCONTEXT_NONE
:
727 case EAXCONTEXT_ALLPARAMETERS
:
728 eax_get_context_all(eax_call
);
731 case EAXCONTEXT_PRIMARYFXSLOTID
:
732 eax_get_primary_fx_slot_id(eax_call
);
735 case EAXCONTEXT_DISTANCEFACTOR
:
736 eax_get_distance_factor(eax_call
);
739 case EAXCONTEXT_AIRABSORPTIONHF
:
740 eax_get_air_absorption_hf(eax_call
);
743 case EAXCONTEXT_HFREFERENCE
:
744 eax_get_hf_reference(eax_call
);
747 case EAXCONTEXT_LASTERROR
:
748 eax_get_last_error(eax_call
);
751 case EAXCONTEXT_SPEAKERCONFIG
:
752 eax_get_speaker_config(eax_call
);
755 case EAXCONTEXT_EAXSESSION
:
756 eax_get_session(eax_call
);
759 case EAXCONTEXT_MACROFXFACTOR
:
760 eax_get_macro_fx_factor(eax_call
);
764 eax_fail("Unsupported property id.");
768 void ALCcontext::eax_set_primary_fx_slot_id()
770 eax_previous_primary_fx_slot_index_
= eax_primary_fx_slot_index_
;
771 eax_primary_fx_slot_index_
= eax_
.context
.guidPrimaryFXSlotID
;
774 void ALCcontext::eax_set_distance_factor()
776 mListener
.mMetersPerUnit
= eax_
.context
.flDistanceFactor
;
780 void ALCcontext::eax_set_air_absorbtion_hf()
782 mAirAbsorptionGainHF
= level_mb_to_gain(eax_
.context
.flAirAbsorptionHF
);
786 void ALCcontext::eax_set_hf_reference()
791 void ALCcontext::eax_set_macro_fx_factor()
796 void ALCcontext::eax_set_context()
798 eax_set_primary_fx_slot_id();
799 eax_set_distance_factor();
800 eax_set_air_absorbtion_hf();
801 eax_set_hf_reference();
804 void ALCcontext::eax_initialize_fx_slots()
806 eax_fx_slots_
.initialize(*this);
807 eax_previous_primary_fx_slot_index_
= eax_
.context
.guidPrimaryFXSlotID
;
808 eax_primary_fx_slot_index_
= eax_
.context
.guidPrimaryFXSlotID
;
811 void ALCcontext::eax_initialize_sources()
813 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
814 auto init_source
= [this](ALsource
&source
) noexcept
815 { source
.eax_initialize(this); };
816 ForEachSource(this, init_source
);
819 void ALCcontext::eax_update_sources()
821 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
822 auto update_source
= [this](ALsource
&source
)
823 { source
.eax_update(eax_context_shared_dirty_flags_
); };
824 ForEachSource(this, update_source
);
827 void ALCcontext::eax_validate_primary_fx_slot_id(
828 const GUID
& primary_fx_slot_id
)
830 if (primary_fx_slot_id
!= EAX_NULL_GUID
&&
831 primary_fx_slot_id
!= EAXPROPERTYID_EAX40_FXSlot0
&&
832 primary_fx_slot_id
!= EAXPROPERTYID_EAX50_FXSlot0
&&
833 primary_fx_slot_id
!= EAXPROPERTYID_EAX40_FXSlot1
&&
834 primary_fx_slot_id
!= EAXPROPERTYID_EAX50_FXSlot1
&&
835 primary_fx_slot_id
!= EAXPROPERTYID_EAX40_FXSlot2
&&
836 primary_fx_slot_id
!= EAXPROPERTYID_EAX50_FXSlot2
&&
837 primary_fx_slot_id
!= EAXPROPERTYID_EAX40_FXSlot3
&&
838 primary_fx_slot_id
!= EAXPROPERTYID_EAX50_FXSlot3
)
840 eax_fail("Unsupported primary FX slot id.");
844 void ALCcontext::eax_validate_distance_factor(
845 float distance_factor
)
847 eax_validate_range
<ContextException
>(
850 EAXCONTEXT_MINDISTANCEFACTOR
,
851 EAXCONTEXT_MAXDISTANCEFACTOR
);
854 void ALCcontext::eax_validate_air_absorption_hf(
855 float air_absorption_hf
)
857 eax_validate_range
<ContextException
>(
860 EAXCONTEXT_MINAIRABSORPTIONHF
,
861 EAXCONTEXT_MAXAIRABSORPTIONHF
);
864 void ALCcontext::eax_validate_hf_reference(
867 eax_validate_range
<ContextException
>(
870 EAXCONTEXT_MINHFREFERENCE
,
871 EAXCONTEXT_MAXHFREFERENCE
);
874 void ALCcontext::eax_validate_speaker_config(
875 unsigned long speaker_config
)
877 switch (speaker_config
)
888 eax_fail("Unsupported speaker configuration.");
892 void ALCcontext::eax_validate_session_eax_version(
893 unsigned long eax_version
)
902 eax_fail("Unsupported session EAX version.");
906 void ALCcontext::eax_validate_session_max_active_sends(
907 unsigned long max_active_sends
)
909 eax_validate_range
<ContextException
>(
912 EAXCONTEXT_MINMAXACTIVESENDS
,
913 EAXCONTEXT_MAXMAXACTIVESENDS
);
916 void ALCcontext::eax_validate_session(
917 const EAXSESSIONPROPERTIES
& eax_session
)
919 eax_validate_session_eax_version(eax_session
.ulEAXVersion
);
920 eax_validate_session_max_active_sends(eax_session
.ulMaxActiveSends
);
923 void ALCcontext::eax_validate_macro_fx_factor(
924 float macro_fx_factor
)
926 eax_validate_range
<ContextException
>(
929 EAXCONTEXT_MINMACROFXFACTOR
,
930 EAXCONTEXT_MAXMACROFXFACTOR
);
933 void ALCcontext::eax_validate_context_all(
934 const EAX40CONTEXTPROPERTIES
& context_all
)
936 eax_validate_primary_fx_slot_id(context_all
.guidPrimaryFXSlotID
);
937 eax_validate_distance_factor(context_all
.flDistanceFactor
);
938 eax_validate_air_absorption_hf(context_all
.flAirAbsorptionHF
);
939 eax_validate_hf_reference(context_all
.flHFReference
);
942 void ALCcontext::eax_validate_context_all(
943 const EAX50CONTEXTPROPERTIES
& context_all
)
945 eax_validate_context_all(static_cast<const EAX40CONTEXTPROPERTIES
>(context_all
));
946 eax_validate_macro_fx_factor(context_all
.flMacroFXFactor
);
949 void ALCcontext::eax_defer_primary_fx_slot_id(
950 const GUID
& primary_fx_slot_id
)
952 eax_d_
.context
.guidPrimaryFXSlotID
= primary_fx_slot_id
;
954 eax_context_dirty_flags_
.guidPrimaryFXSlotID
=
955 (eax_
.context
.guidPrimaryFXSlotID
!= eax_d_
.context
.guidPrimaryFXSlotID
);
958 void ALCcontext::eax_defer_distance_factor(
959 float distance_factor
)
961 eax_d_
.context
.flDistanceFactor
= distance_factor
;
963 eax_context_dirty_flags_
.flDistanceFactor
=
964 (eax_
.context
.flDistanceFactor
!= eax_d_
.context
.flDistanceFactor
);
967 void ALCcontext::eax_defer_air_absorption_hf(
968 float air_absorption_hf
)
970 eax_d_
.context
.flAirAbsorptionHF
= air_absorption_hf
;
972 eax_context_dirty_flags_
.flAirAbsorptionHF
=
973 (eax_
.context
.flAirAbsorptionHF
!= eax_d_
.context
.flAirAbsorptionHF
);
976 void ALCcontext::eax_defer_hf_reference(
979 eax_d_
.context
.flHFReference
= hf_reference
;
981 eax_context_dirty_flags_
.flHFReference
=
982 (eax_
.context
.flHFReference
!= eax_d_
.context
.flHFReference
);
985 void ALCcontext::eax_defer_macro_fx_factor(
986 float macro_fx_factor
)
988 eax_d_
.context
.flMacroFXFactor
= macro_fx_factor
;
990 eax_context_dirty_flags_
.flMacroFXFactor
=
991 (eax_
.context
.flMacroFXFactor
!= eax_d_
.context
.flMacroFXFactor
);
994 void ALCcontext::eax_defer_context_all(
995 const EAX40CONTEXTPROPERTIES
& context_all
)
997 eax_defer_primary_fx_slot_id(context_all
.guidPrimaryFXSlotID
);
998 eax_defer_distance_factor(context_all
.flDistanceFactor
);
999 eax_defer_air_absorption_hf(context_all
.flAirAbsorptionHF
);
1000 eax_defer_hf_reference(context_all
.flHFReference
);
1003 void ALCcontext::eax_defer_context_all(
1004 const EAX50CONTEXTPROPERTIES
& context_all
)
1006 eax_defer_context_all(static_cast<const EAX40CONTEXTPROPERTIES
&>(context_all
));
1007 eax_defer_macro_fx_factor(context_all
.flMacroFXFactor
);
1010 void ALCcontext::eax_defer_context_all(
1011 const EaxEaxCall
& eax_call
)
1013 switch(eax_call
.get_version())
1017 const auto& context_all
=
1018 eax_call
.get_value
<ContextException
, EAX40CONTEXTPROPERTIES
>();
1020 eax_validate_context_all(context_all
);
1021 eax_defer_context_all(context_all
);
1027 const auto& context_all
=
1028 eax_call
.get_value
<ContextException
, EAX50CONTEXTPROPERTIES
>();
1030 eax_validate_context_all(context_all
);
1031 eax_defer_context_all(context_all
);
1036 eax_fail("Unsupported EAX version.");
1040 void ALCcontext::eax_defer_primary_fx_slot_id(
1041 const EaxEaxCall
& eax_call
)
1043 const auto& primary_fx_slot_id
=
1044 eax_call
.get_value
<ContextException
, const decltype(EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID
)>();
1046 eax_validate_primary_fx_slot_id(primary_fx_slot_id
);
1047 eax_defer_primary_fx_slot_id(primary_fx_slot_id
);
1050 void ALCcontext::eax_defer_distance_factor(
1051 const EaxEaxCall
& eax_call
)
1053 const auto& distance_factor
=
1054 eax_call
.get_value
<ContextException
, const decltype(EAX50CONTEXTPROPERTIES::flDistanceFactor
)>();
1056 eax_validate_distance_factor(distance_factor
);
1057 eax_defer_distance_factor(distance_factor
);
1060 void ALCcontext::eax_defer_air_absorption_hf(
1061 const EaxEaxCall
& eax_call
)
1063 const auto& air_absorption_hf
=
1064 eax_call
.get_value
<ContextException
, const decltype(EAX50CONTEXTPROPERTIES::flAirAbsorptionHF
)>();
1066 eax_validate_air_absorption_hf(air_absorption_hf
);
1067 eax_defer_air_absorption_hf(air_absorption_hf
);
1070 void ALCcontext::eax_defer_hf_reference(
1071 const EaxEaxCall
& eax_call
)
1073 const auto& hf_reference
=
1074 eax_call
.get_value
<ContextException
, const decltype(EAX50CONTEXTPROPERTIES::flHFReference
)>();
1076 eax_validate_hf_reference(hf_reference
);
1077 eax_defer_hf_reference(hf_reference
);
1080 void ALCcontext::eax_set_session(
1081 const EaxEaxCall
& eax_call
)
1083 const auto& eax_session
=
1084 eax_call
.get_value
<ContextException
, const EAXSESSIONPROPERTIES
>();
1086 eax_validate_session(eax_session
);
1088 eax_session_
= eax_session
;
1091 void ALCcontext::eax_defer_macro_fx_factor(
1092 const EaxEaxCall
& eax_call
)
1094 const auto& macro_fx_factor
=
1095 eax_call
.get_value
<ContextException
, const decltype(EAX50CONTEXTPROPERTIES::flMacroFXFactor
)>();
1097 eax_validate_macro_fx_factor(macro_fx_factor
);
1098 eax_defer_macro_fx_factor(macro_fx_factor
);
1101 void ALCcontext::eax_set(
1102 const EaxEaxCall
& eax_call
)
1104 switch (eax_call
.get_property_id())
1106 case EAXCONTEXT_NONE
:
1109 case EAXCONTEXT_ALLPARAMETERS
:
1110 eax_defer_context_all(eax_call
);
1113 case EAXCONTEXT_PRIMARYFXSLOTID
:
1114 eax_defer_primary_fx_slot_id(eax_call
);
1117 case EAXCONTEXT_DISTANCEFACTOR
:
1118 eax_defer_distance_factor(eax_call
);
1121 case EAXCONTEXT_AIRABSORPTIONHF
:
1122 eax_defer_air_absorption_hf(eax_call
);
1125 case EAXCONTEXT_HFREFERENCE
:
1126 eax_defer_hf_reference(eax_call
);
1129 case EAXCONTEXT_LASTERROR
:
1130 eax_fail("Last error is read-only.");
1132 case EAXCONTEXT_SPEAKERCONFIG
:
1133 eax_fail("Speaker configuration is read-only.");
1135 case EAXCONTEXT_EAXSESSION
:
1136 eax_set_session(eax_call
);
1139 case EAXCONTEXT_MACROFXFACTOR
:
1140 eax_defer_macro_fx_factor(eax_call
);
1144 eax_fail("Unsupported property id.");
1148 void ALCcontext::eax_apply_deferred()
1150 if (eax_context_dirty_flags_
== ContextDirtyFlags
{})
1157 if (eax_context_dirty_flags_
.guidPrimaryFXSlotID
)
1159 eax_context_shared_dirty_flags_
.primary_fx_slot_id
= true;
1160 eax_set_primary_fx_slot_id();
1163 if (eax_context_dirty_flags_
.flDistanceFactor
)
1165 eax_set_distance_factor();
1168 if (eax_context_dirty_flags_
.flAirAbsorptionHF
)
1170 eax_set_air_absorbtion_hf();
1173 if (eax_context_dirty_flags_
.flHFReference
)
1175 eax_set_hf_reference();
1178 if (eax_context_dirty_flags_
.flMacroFXFactor
)
1180 eax_set_macro_fx_factor();
1183 if (eax_context_shared_dirty_flags_
!= EaxContextSharedDirtyFlags
{})
1185 eax_update_sources();
1188 eax_context_shared_dirty_flags_
= EaxContextSharedDirtyFlags
{};
1189 eax_context_dirty_flags_
= ContextDirtyFlags
{};
1197 class EaxSetException
:
1201 explicit EaxSetException(
1202 const char* message
)
1204 EaxException
{"EAX_SET", message
}
1207 }; // EaxSetException
1212 const char* message
)
1214 throw EaxSetException
{message
};
1218 class EaxGetException
:
1222 explicit EaxGetException(
1223 const char* message
)
1225 EaxException
{"EAX_GET", message
}
1228 }; // EaxGetException
1233 const char* message
)
1235 throw EaxGetException
{message
};
1242 FORCE_ALIGN ALenum AL_APIENTRY
EAXSet(
1243 const GUID
* property_set_id
,
1245 ALuint property_source_id
,
1246 ALvoid
* property_value
,
1247 ALuint property_value_size
) noexcept
1250 auto context
= GetContextRef();
1254 eax_fail_set("No current context.");
1257 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1259 return context
->eax_eax_set(
1269 eax_log_exception(__func__
);
1270 return AL_INVALID_OPERATION
;
1273 FORCE_ALIGN ALenum AL_APIENTRY
EAXGet(
1274 const GUID
* property_set_id
,
1276 ALuint property_source_id
,
1277 ALvoid
* property_value
,
1278 ALuint property_value_size
) noexcept
1281 auto context
= GetContextRef();
1285 eax_fail_get("No current context.");
1288 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1290 return context
->eax_eax_get(
1300 eax_log_exception(__func__
);
1301 return AL_INVALID_OPERATION
;
1303 #endif // ALSOFT_EAX