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_STATIC_BUFFER "
61 "AL_EXT_STEREO_ANGLES "
62 "AL_LOKI_quadriphonic "
64 "AL_SOFTX_bformat_hoa "
65 "AL_SOFT_block_alignment "
66 "AL_SOFT_buffer_length_query "
67 "AL_SOFT_callback_buffer "
68 "AL_SOFTX_convolution_reverb "
69 "AL_SOFT_deferred_updates "
70 "AL_SOFT_direct_channels "
71 "AL_SOFT_direct_channels_remix "
72 "AL_SOFT_effect_target "
74 "AL_SOFT_gain_clamp_ex "
75 "AL_SOFTX_hold_on_disconnect "
76 "AL_SOFT_loop_points "
77 "AL_SOFTX_map_buffer "
79 "AL_SOFT_source_latency "
80 "AL_SOFT_source_length "
81 "AL_SOFT_source_resampler "
82 "AL_SOFT_source_spatialize "
83 "AL_SOFT_source_start_delay "
90 std::atomic<bool> ALCcontext::sGlobalContextLock{false};
91 std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
93 thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
94 ALCcontext::ThreadCtx::~ThreadCtx()
96 if(ALCcontext *ctx{ALCcontext::sLocalContext})
98 const bool result{ctx->releaseIfNoDelete()};
99 ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
100 result ? "" : ", leak detected");
103 thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
105 ALeffect ALCcontext::sDefaultEffect;
109 ALCcontext *ALCcontext::getThreadContext() noexcept
110 { return sLocalContext; }
111 void ALCcontext::setThreadContext(ALCcontext *context) noexcept
112 { sThreadContext.set(context); }
115 ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
116 : ContextBase{device.get()}, mALDevice{std::move(device)}
120 ALCcontext::~ALCcontext()
122 TRACE("Freeing context %p\n", voidp{this});
124 size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u},
125 [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
126 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
128 WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
136 mDefaultSlot = nullptr;
137 count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
138 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
139 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
141 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
142 mEffectSlotList.clear();
146 void ALCcontext::init()
148 if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
150 mDefaultSlot = std::make_unique<ALeffectslot>(this);
151 aluInitEffectPanning(mDefaultSlot->mSlot, this);
154 EffectSlotArray *auxslots;
156 auxslots = EffectSlot::CreatePtrArray(0);
159 auxslots = EffectSlot::CreatePtrArray(1);
160 (*auxslots)[0] = mDefaultSlot->mSlot;
161 mDefaultSlot->mState = SlotState::Playing;
163 mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
167 VoiceChange *cur{mVoiceChangeTail};
168 while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
170 mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
173 mExtensionList = alExtList;
175 if(sBufferSubDataCompat)
177 std::string extlist{mExtensionList};
179 const auto pos = extlist.find("AL_EXT_SOURCE_RADIUS ");
180 if(pos != std::string::npos)
181 extlist.replace(pos, 20, "AL_SOFT_buffer_sub_data");
183 extlist += " AL_SOFT_buffer_sub_data";
185 mExtensionListOverride = std::move(extlist);
186 mExtensionList = mExtensionListOverride.c_str();
190 eax_initialize_extensions();
193 mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
194 mParams.Matrix = alu::Matrix::Identity();
195 mParams.Velocity = alu::Vector{};
196 mParams.Gain = mListener.Gain;
197 mParams.MetersPerUnit = mListener.mMetersPerUnit;
198 mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
199 mParams.DopplerFactor = mDopplerFactor;
200 mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
201 mParams.SourceDistanceModel = mSourceDistanceModel;
202 mParams.mDistanceModel = mDistanceModel;
205 mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
206 StartEventThrd(this);
210 mActiveVoiceCount.store(64, std::memory_order_relaxed);
213 bool ALCcontext::deinit()
215 if(sLocalContext == this)
217 WARN("%p released while current on thread\n", voidp{this});
218 sThreadContext.set(nullptr);
222 ALCcontext *origctx{this};
223 if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
225 while(sGlobalContextLock.load()) {
226 /* Wait to make sure another thread didn't get the context and is
227 * trying to increment its refcount.
234 /* First make sure this context exists in the device's list. */
235 auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
236 if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
238 using ContextArray = al::FlexArray<ContextBase*>;
239 auto alloc_ctx_array = [](const size_t count) -> ContextArray*
241 if(count == 0) return &DeviceBase::sEmptyContextArray;
242 return ContextArray::Create(count).release();
244 auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
246 /* Copy the current/old context handles to the new array, excluding the
249 std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
250 [this](ContextBase *ctx) { return ctx != this; });
252 /* Store the new context array in the device. Wait for any current mix
253 * to finish before deleting the old array.
255 mDevice->mContexts.store(newarray);
256 if(oldarray != &DeviceBase::sEmptyContextArray)
258 mDevice->waitForMix();
262 ret = !newarray->empty();
265 ret = !oldarray->empty();
272 void ALCcontext::applyAllUpdates()
274 /* Tell the mixer to stop applying updates, then wait for any active
275 * updating to finish, before providing updates.
277 mHoldUpdates.store(true, std::memory_order_release);
278 while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
287 if(std::exchange(mPropsDirty, false))
288 UpdateContextProps(this);
289 UpdateAllEffectSlotProps(this);
290 UpdateAllSourceProps(this);
292 /* Now with all updates declared, let the mixer continue applying them so
293 * they all happen at once.
295 mHoldUpdates.store(false, std::memory_order_release);
302 void ForEachSource(ALCcontext *context, F func)
304 for(auto &sublist : context->mSourceList)
306 uint64_t usemask{~sublist.FreeMask};
309 const int idx{al::countr_zero(usemask)};
310 usemask &= ~(1_u64 << idx);
312 func(sublist.Sources[idx]);
320 bool ALCcontext::eaxIsCapable() const noexcept
322 return eax_has_enough_aux_sends();
325 void ALCcontext::eaxUninitialize() noexcept
327 if(!mEaxIsInitialized)
330 mEaxIsInitialized = false;
332 mEaxFxSlots.uninitialize();
335 ALenum ALCcontext::eax_eax_set(
336 const GUID* property_set_id,
338 ALuint property_source_id,
339 ALvoid* property_value,
340 ALuint property_value_size)
342 const auto call = create_eax_call(
348 property_value_size);
352 switch(call.get_property_set_id())
354 case EaxCallPropertySetId::context:
357 case EaxCallPropertySetId::fx_slot:
358 case EaxCallPropertySetId::fx_slot_effect:
359 eax_dispatch_fx_slot(call);
361 case EaxCallPropertySetId::source:
362 eax_dispatch_source(call);
365 eax_fail_unknown_property_set_id();
367 mEaxNeedsCommit = true;
369 if(!call.is_deferred())
379 ALenum ALCcontext::eax_eax_get(
380 const GUID* property_set_id,
382 ALuint property_source_id,
383 ALvoid* property_value,
384 ALuint property_value_size)
386 const auto call = create_eax_call(
392 property_value_size);
396 switch(call.get_property_set_id())
398 case EaxCallPropertySetId::context:
401 case EaxCallPropertySetId::fx_slot:
402 case EaxCallPropertySetId::fx_slot_effect:
403 eax_dispatch_fx_slot(call);
405 case EaxCallPropertySetId::source:
406 eax_dispatch_source(call);
409 eax_fail_unknown_property_set_id();
415 void ALCcontext::eaxSetLastError() noexcept
417 mEaxLastError = EAXERR_INVALID_OPERATION;
420 [[noreturn]] void ALCcontext::eax_fail(const char* message)
422 throw ContextException{message};
425 [[noreturn]] void ALCcontext::eax_fail_unknown_property_set_id()
427 eax_fail("Unknown property ID.");
430 [[noreturn]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
432 eax_fail("Unknown primary FX Slot ID.");
435 [[noreturn]] void ALCcontext::eax_fail_unknown_property_id()
437 eax_fail("Unknown property ID.");
440 [[noreturn]] void ALCcontext::eax_fail_unknown_version()
442 eax_fail("Unknown version.");
445 void ALCcontext::eax_initialize_extensions()
447 if(!eax_g_is_enabled)
450 const auto string_max_capacity =
451 std::strlen(mExtensionList) + 1 +
452 std::strlen(eax1_ext_name) + 1 +
453 std::strlen(eax2_ext_name) + 1 +
454 std::strlen(eax3_ext_name) + 1 +
455 std::strlen(eax4_ext_name) + 1 +
456 std::strlen(eax5_ext_name) + 1 +
457 std::strlen(eax_x_ram_ext_name) + 1;
460 extlist.reserve(string_max_capacity);
464 extlist += eax1_ext_name;
467 extlist += eax2_ext_name;
470 extlist += eax3_ext_name;
473 extlist += eax4_ext_name;
476 extlist += eax5_ext_name;
480 extlist += eax_x_ram_ext_name;
483 extlist += mExtensionList;
485 mExtensionListOverride = std::move(extlist);
486 mExtensionList = mExtensionListOverride.c_str();
489 void ALCcontext::eax_initialize()
491 if(mEaxIsInitialized)
499 if(!eax_g_is_enabled)
500 eax_fail("EAX disabled by a configuration.");
502 eax_ensure_compatibility();
504 eax_context_commit_air_absorbtion_hf();
505 eax_update_speaker_configuration();
506 eax_initialize_fx_slots();
508 mEaxIsInitialized = true;
511 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
513 return mDefaultSlot == nullptr;
516 void ALCcontext::eax_ensure_no_default_effect_slot() const
518 if(!eax_has_no_default_effect_slot())
519 eax_fail("There is a default effect slot in the context.");
522 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
524 return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS;
527 void ALCcontext::eax_ensure_enough_aux_sends() const
529 if(!eax_has_enough_aux_sends())
530 eax_fail("Not enough aux sends.");
533 void ALCcontext::eax_ensure_compatibility()
535 eax_ensure_enough_aux_sends();
538 unsigned long ALCcontext::eax_detect_speaker_configuration() const
540 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
542 switch(mDevice->FmtChans)
544 case DevFmtMono: return SPEAKERS_2;
546 /* Pretend 7.1 if using UHJ output, since they both provide full
547 * horizontal surround.
549 if(mDevice->mUhjEncoder)
551 if(mDevice->Flags.test(DirectEar))
554 case DevFmtQuad: return SPEAKERS_4;
555 case DevFmtX51: return SPEAKERS_5;
556 case DevFmtX61: return SPEAKERS_6;
557 case DevFmtX71: return SPEAKERS_7;
558 /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
559 * suggest with-height surround sound (like HRTF).
561 case DevFmtX714: return SPEAKERS_7;
562 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
563 * suggest full-sphere surround sound (like HRTF).
565 case DevFmtX3D71: return SPEAKERS_5;
566 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
567 * provide full-sphere surround sound. Depends if apps are more likely to
568 * consider headphones or 7.1 for surround sound support.
570 case DevFmtAmbi3D: return SPEAKERS_7;
572 ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
578 void ALCcontext::eax_update_speaker_configuration()
580 mEaxSpeakerConfig = eax_detect_speaker_configuration();
583 void ALCcontext::eax_set_last_error_defaults() noexcept
585 mEaxLastError = EAX_OK;
588 void ALCcontext::eax_session_set_defaults() noexcept
590 mEaxSession.ulEAXVersion = EAXCONTEXT_DEFAULTEAXSESSION;
591 mEaxSession.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
594 void ALCcontext::eax4_context_set_defaults(Eax4Props& props) noexcept
596 props.guidPrimaryFXSlotID = EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
597 props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
598 props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
599 props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
602 void ALCcontext::eax4_context_set_defaults(Eax4State& state) noexcept
604 eax4_context_set_defaults(state.i);
608 void ALCcontext::eax5_context_set_defaults(Eax5Props& props) noexcept
610 props.guidPrimaryFXSlotID = EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID;
611 props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
612 props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
613 props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
614 props.flMacroFXFactor = EAXCONTEXT_DEFAULTMACROFXFACTOR;
617 void ALCcontext::eax5_context_set_defaults(Eax5State& state) noexcept
619 eax5_context_set_defaults(state.i);
623 void ALCcontext::eax_context_set_defaults()
625 eax5_context_set_defaults(mEax123);
626 eax4_context_set_defaults(mEax4);
627 eax5_context_set_defaults(mEax5);
630 mEaxDf = EaxDirtyFlags{};
633 void ALCcontext::eax_set_defaults()
635 eax_set_last_error_defaults();
636 eax_session_set_defaults();
637 eax_context_set_defaults();
640 void ALCcontext::eax_dispatch_fx_slot(const EaxCall& call)
642 const auto fx_slot_index = call.get_fx_slot_index();
643 if(!fx_slot_index.has_value())
644 eax_fail("Invalid fx slot index.");
646 auto& fx_slot = eaxGetFxSlot(*fx_slot_index);
647 if(fx_slot.eax_dispatch(call))
649 std::lock_guard<std::mutex> source_lock{mSourceLock};
650 ForEachSource(this, std::mem_fn(&ALsource::eaxMarkAsChanged));
654 void ALCcontext::eax_dispatch_source(const EaxCall& call)
656 const auto source_id = call.get_property_al_name();
657 std::lock_guard<std::mutex> source_lock{mSourceLock};
658 const auto source = ALsource::EaxLookupSource(*this, source_id);
660 if (source == nullptr)
661 eax_fail("Source not found.");
663 source->eaxDispatch(call);
666 void ALCcontext::eax_get_misc(const EaxCall& call)
668 switch(call.get_property_id())
670 case EAXCONTEXT_NONE:
672 case EAXCONTEXT_LASTERROR:
673 call.set_value<ContextException>(mEaxLastError);
675 case EAXCONTEXT_SPEAKERCONFIG:
676 call.set_value<ContextException>(mEaxSpeakerConfig);
678 case EAXCONTEXT_EAXSESSION:
679 call.set_value<ContextException>(mEaxSession);
682 eax_fail_unknown_property_id();
686 void ALCcontext::eax4_get(const EaxCall& call, const Eax4Props& props)
688 switch(call.get_property_id())
690 case EAXCONTEXT_ALLPARAMETERS:
691 call.set_value<ContextException>(props);
693 case EAXCONTEXT_PRIMARYFXSLOTID:
694 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
696 case EAXCONTEXT_DISTANCEFACTOR:
697 call.set_value<ContextException>(props.flDistanceFactor);
699 case EAXCONTEXT_AIRABSORPTIONHF:
700 call.set_value<ContextException>(props.flAirAbsorptionHF);
702 case EAXCONTEXT_HFREFERENCE:
703 call.set_value<ContextException>(props.flHFReference);
711 void ALCcontext::eax5_get(const EaxCall& call, const Eax5Props& props)
713 switch(call.get_property_id())
715 case EAXCONTEXT_ALLPARAMETERS:
716 call.set_value<ContextException>(props);
718 case EAXCONTEXT_PRIMARYFXSLOTID:
719 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
721 case EAXCONTEXT_DISTANCEFACTOR:
722 call.set_value<ContextException>(props.flDistanceFactor);
724 case EAXCONTEXT_AIRABSORPTIONHF:
725 call.set_value<ContextException>(props.flAirAbsorptionHF);
727 case EAXCONTEXT_HFREFERENCE:
728 call.set_value<ContextException>(props.flHFReference);
730 case EAXCONTEXT_MACROFXFACTOR:
731 call.set_value<ContextException>(props.flMacroFXFactor);
739 void ALCcontext::eax_get(const EaxCall& call)
741 switch(call.get_version())
743 case 4: eax4_get(call, mEax4.i); break;
744 case 5: eax5_get(call, mEax5.i); break;
745 default: eax_fail_unknown_version();
749 void ALCcontext::eax_context_commit_primary_fx_slot_id()
751 mEaxPrimaryFxSlotIndex = mEax.guidPrimaryFXSlotID;
754 void ALCcontext::eax_context_commit_distance_factor()
756 if(mListener.mMetersPerUnit == mEax.flDistanceFactor)
759 mListener.mMetersPerUnit = mEax.flDistanceFactor;
763 void ALCcontext::eax_context_commit_air_absorbtion_hf()
765 const auto new_value = level_mb_to_gain(mEax.flAirAbsorptionHF);
767 if(mAirAbsorptionGainHF == new_value)
770 mAirAbsorptionGainHF = new_value;
774 void ALCcontext::eax_context_commit_hf_reference()
779 void ALCcontext::eax_context_commit_macro_fx_factor()
784 void ALCcontext::eax_initialize_fx_slots()
786 mEaxFxSlots.initialize(*this);
787 mEaxPrimaryFxSlotIndex = mEax.guidPrimaryFXSlotID;
790 void ALCcontext::eax_update_sources()
792 std::unique_lock<std::mutex> source_lock{mSourceLock};
793 auto update_source = [](ALsource &source)
794 { source.eaxCommit(); };
795 ForEachSource(this, update_source);
798 void ALCcontext::eax_set_misc(const EaxCall& call)
800 switch(call.get_property_id())
802 case EAXCONTEXT_NONE:
804 case EAXCONTEXT_SPEAKERCONFIG:
805 eax_set<Eax5SpeakerConfigValidator>(call, mEaxSpeakerConfig);
807 case EAXCONTEXT_EAXSESSION:
808 eax_set<Eax5SessionAllValidator>(call, mEaxSession);
811 eax_fail_unknown_property_id();
815 void ALCcontext::eax4_defer_all(const EaxCall& call, Eax4State& state)
817 const auto& src = call.get_value<ContextException, const EAX40CONTEXTPROPERTIES>();
818 Eax4AllValidator{}(src);
819 const auto& dst_i = state.i;
820 auto& dst_d = state.d;
823 if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
824 mEaxDf |= eax_primary_fx_slot_id_dirty_bit;
826 if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
827 mEaxDf |= eax_distance_factor_dirty_bit;
829 if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
830 mEaxDf |= eax_air_absorption_hf_dirty_bit;
832 if(dst_i.flHFReference != dst_d.flHFReference)
833 mEaxDf |= eax_hf_reference_dirty_bit;
836 void ALCcontext::eax4_defer(const EaxCall& call, Eax4State& state)
838 switch(call.get_property_id())
840 case EAXCONTEXT_ALLPARAMETERS:
841 eax4_defer_all(call, state);
843 case EAXCONTEXT_PRIMARYFXSLOTID:
844 eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
845 call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
847 case EAXCONTEXT_DISTANCEFACTOR:
848 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
849 call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
851 case EAXCONTEXT_AIRABSORPTIONHF:
852 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
853 call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
855 case EAXCONTEXT_HFREFERENCE:
856 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
857 call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
865 void ALCcontext::eax5_defer_all(const EaxCall& call, Eax5State& state)
867 const auto& src = call.get_value<ContextException, const EAX50CONTEXTPROPERTIES>();
868 Eax4AllValidator{}(src);
869 const auto& dst_i = state.i;
870 auto& dst_d = state.d;
873 if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
874 mEaxDf |= eax_primary_fx_slot_id_dirty_bit;
876 if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
877 mEaxDf |= eax_distance_factor_dirty_bit;
879 if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
880 mEaxDf |= eax_air_absorption_hf_dirty_bit;
882 if(dst_i.flHFReference != dst_d.flHFReference)
883 mEaxDf |= eax_hf_reference_dirty_bit;
885 if(dst_i.flMacroFXFactor != dst_d.flMacroFXFactor)
886 mEaxDf |= eax_macro_fx_factor_dirty_bit;
889 void ALCcontext::eax5_defer(const EaxCall& call, Eax5State& state)
891 switch(call.get_property_id())
893 case EAXCONTEXT_ALLPARAMETERS:
894 eax5_defer_all(call, state);
896 case EAXCONTEXT_PRIMARYFXSLOTID:
897 eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
898 call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
900 case EAXCONTEXT_DISTANCEFACTOR:
901 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
902 call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
904 case EAXCONTEXT_AIRABSORPTIONHF:
905 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
906 call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
908 case EAXCONTEXT_HFREFERENCE:
909 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
910 call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
912 case EAXCONTEXT_MACROFXFACTOR:
913 eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
914 call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
922 void ALCcontext::eax_set(const EaxCall& call)
924 const auto version = call.get_version();
927 case 4: eax4_defer(call, mEax4); break;
928 case 5: eax5_defer(call, mEax5); break;
929 default: eax_fail_unknown_version();
931 if(version != mEaxVersion)
932 mEaxDf = ~EaxDirtyFlags();
933 mEaxVersion = version;
936 void ALCcontext::eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df)
938 if(mEaxDf == EaxDirtyFlags{})
941 eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
942 state, dst_df, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
943 eax_context_commit_property<eax_distance_factor_dirty_bit>(
944 state, dst_df, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
945 eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
946 state, dst_df, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
947 eax_context_commit_property<eax_hf_reference_dirty_bit>(
948 state, dst_df, &EAX40CONTEXTPROPERTIES::flHFReference);
950 mEaxDf = EaxDirtyFlags{};
953 void ALCcontext::eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df)
955 if(mEaxDf == EaxDirtyFlags{})
958 eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
959 state, dst_df, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
960 eax_context_commit_property<eax_distance_factor_dirty_bit>(
961 state, dst_df, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
962 eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
963 state, dst_df, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
964 eax_context_commit_property<eax_hf_reference_dirty_bit>(
965 state, dst_df, &EAX50CONTEXTPROPERTIES::flHFReference);
966 eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(
967 state, dst_df, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
969 mEaxDf = EaxDirtyFlags{};
972 void ALCcontext::eax_context_commit()
974 auto dst_df = EaxDirtyFlags{};
981 eax5_context_commit(mEax123, dst_df);
984 eax4_context_commit(mEax4, dst_df);
987 eax5_context_commit(mEax5, dst_df);
991 if(dst_df == EaxDirtyFlags{})
994 if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
995 eax_context_commit_primary_fx_slot_id();
997 if((dst_df & eax_distance_factor_dirty_bit) != EaxDirtyFlags{})
998 eax_context_commit_distance_factor();
1000 if((dst_df & eax_air_absorption_hf_dirty_bit) != EaxDirtyFlags{})
1001 eax_context_commit_air_absorbtion_hf();
1003 if((dst_df & eax_hf_reference_dirty_bit) != EaxDirtyFlags{})
1004 eax_context_commit_hf_reference();
1006 if((dst_df & eax_macro_fx_factor_dirty_bit) != EaxDirtyFlags{})
1007 eax_context_commit_macro_fx_factor();
1009 if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
1010 eax_update_sources();
1013 void ALCcontext::eaxCommit()
1015 mEaxNeedsCommit = false;
1016 eax_context_commit();
1018 eax_update_sources();
1023 class EaxSetException : public EaxException {
1025 explicit EaxSetException(const char* message)
1026 : EaxException{"EAX_SET", message}
1030 [[noreturn]] void eax_fail_set(const char* message)
1032 throw EaxSetException{message};
1035 class EaxGetException : public EaxException {
1037 explicit EaxGetException(const char* message)
1038 : EaxException{"EAX_GET", message}
1042 [[noreturn]] void eax_fail_get(const char* message)
1044 throw EaxGetException{message};
1050 FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
1051 const GUID* property_set_id,
1053 ALuint property_source_id,
1054 ALvoid* property_value,
1055 ALuint property_value_size) noexcept
1058 auto context = GetContextRef();
1061 eax_fail_set("No current context.");
1063 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1065 return context->eax_eax_set(
1070 property_value_size);
1074 eax_log_exception(__func__);
1075 return AL_INVALID_OPERATION;
1078 FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
1079 const GUID* property_set_id,
1081 ALuint property_source_id,
1082 ALvoid* property_value,
1083 ALuint property_value_size) noexcept
1086 auto context = GetContextRef();
1089 eax_fail_get("No current context.");
1091 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1093 return context->eax_eax_get(
1098 property_value_size);
1102 eax_log_exception(__func__);
1103 return AL_INVALID_OPERATION;
1105 #endif // ALSOFT_EAX