13 #include <string_view>
19 #include "al/auxeffectslot.h"
21 #include "al/source.h"
22 #include "al/effect.h"
24 #include "al/listener.h"
27 #include "alc/backends/base.h"
28 #include "alnumeric.h"
31 #include "core/async_event.h"
32 #include "core/devformat.h"
33 #include "core/device.h"
34 #include "core/effectslot.h"
35 #include "core/logging.h"
36 #include "core/voice_change.h"
38 #include "flexarray.h"
39 #include "ringbuffer.h"
43 #include "al/eax/call.h"
44 #include "al/eax/globals.h"
49 using namespace std::string_view_literals
;
52 /* Default context extensions */
53 std::vector
<std::string_view
> getContextExtensions() noexcept
55 return std::vector
<std::string_view
>{
59 "AL_EXT_direct_context"sv
,
61 "AL_EXT_EXPONENT_DISTANCE"sv
,
64 "AL_EXT_LINEAR_DISTANCE"sv
,
67 "AL_EXT_MULAW_BFORMAT"sv
,
68 "AL_EXT_MULAW_MCFORMATS"sv
,
70 "AL_EXT_source_distance_model"sv
,
71 "AL_EXT_SOURCE_RADIUS"sv
,
72 "AL_EXT_STATIC_BUFFER"sv
,
73 "AL_EXT_STEREO_ANGLES"sv
,
74 "AL_LOKI_quadriphonic"sv
,
75 "AL_SOFT_bformat_ex"sv
,
76 "AL_SOFTX_bformat_hoa"sv
,
77 "AL_SOFT_block_alignment"sv
,
78 "AL_SOFT_buffer_length_query"sv
,
79 "AL_SOFT_callback_buffer"sv
,
80 "AL_SOFTX_convolution_effect"sv
,
81 "AL_SOFT_deferred_updates"sv
,
82 "AL_SOFT_direct_channels"sv
,
83 "AL_SOFT_direct_channels_remix"sv
,
84 "AL_SOFT_effect_target"sv
,
86 "AL_SOFT_gain_clamp_ex"sv
,
87 "AL_SOFTX_hold_on_disconnect"sv
,
88 "AL_SOFT_loop_points"sv
,
89 "AL_SOFTX_map_buffer"sv
,
91 "AL_SOFT_source_latency"sv
,
92 "AL_SOFT_source_length"sv
,
93 "AL_SOFTX_source_panning"sv
,
94 "AL_SOFT_source_resampler"sv
,
95 "AL_SOFT_source_spatialize"sv
,
96 "AL_SOFT_source_start_delay"sv
,
105 std::atomic
<bool> ALCcontext::sGlobalContextLock
{false};
106 std::atomic
<ALCcontext
*> ALCcontext::sGlobalContext
{nullptr};
108 ALCcontext::ThreadCtx::~ThreadCtx()
110 if(ALCcontext
*ctx
{std::exchange(ALCcontext::sLocalContext
, nullptr)})
112 const bool result
{ctx
->releaseIfNoDelete()};
113 ERR("Context %p current for thread being destroyed%s!\n", voidp
{ctx
},
114 result
? "" : ", leak detected");
117 thread_local
ALCcontext::ThreadCtx
ALCcontext::sThreadContext
;
119 ALeffect
ALCcontext::sDefaultEffect
;
122 ALCcontext::ALCcontext(al::intrusive_ptr
<al::Device
> device
, ContextFlagBitset flags
)
123 : ContextBase
{device
.get()}, mALDevice
{std::move(device
)}, mContextFlags
{flags
}
125 mDebugGroups
.emplace_back(DebugSource::Other
, 0, std::string
{});
126 mDebugEnabled
.store(mContextFlags
.test(ContextFlags::DebugBit
), std::memory_order_relaxed
);
128 /* Low-severity debug messages are disabled by default. */
129 alDebugMessageControlDirectEXT(this, AL_DONT_CARE_EXT
, AL_DONT_CARE_EXT
,
130 AL_DEBUG_SEVERITY_LOW_EXT
, 0, nullptr, AL_FALSE
);
133 ALCcontext::~ALCcontext()
135 TRACE("Freeing context %p\n", voidp
{this});
137 size_t count
{std::accumulate(mSourceList
.cbegin(), mSourceList
.cend(), 0_uz
,
138 [](size_t cur
, const SourceSubList
&sublist
) noexcept
-> size_t
139 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); })};
141 WARN("%zu Source%s not deleted\n", count
, (count
==1)?"":"s");
149 mDefaultSlot
= nullptr;
150 count
= std::accumulate(mEffectSlotList
.cbegin(), mEffectSlotList
.cend(), 0_uz
,
151 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
152 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); });
154 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count
, (count
==1)?"":"s");
155 mEffectSlotList
.clear();
159 void ALCcontext::init()
161 if(sDefaultEffect
.type
!= AL_EFFECT_NULL
&& mDevice
->Type
== DeviceType::Playback
)
163 mDefaultSlot
= std::make_unique
<ALeffectslot
>(this);
164 aluInitEffectPanning(mDefaultSlot
->mSlot
, this);
167 std::unique_ptr
<EffectSlotArray
> auxslots
;
169 auxslots
= EffectSlot::CreatePtrArray(0);
172 auxslots
= EffectSlot::CreatePtrArray(2);
173 (*auxslots
)[0] = mDefaultSlot
->mSlot
;
174 (*auxslots
)[1] = mDefaultSlot
->mSlot
;
175 mDefaultSlot
->mState
= SlotState::Playing
;
177 mActiveAuxSlots
.store(std::move(auxslots
), std::memory_order_relaxed
);
181 VoiceChange
*cur
{mVoiceChangeTail
};
182 while(VoiceChange
*next
{cur
->mNext
.load(std::memory_order_relaxed
)})
184 mCurrentVoiceChange
.store(cur
, std::memory_order_relaxed
);
187 mExtensions
= getContextExtensions();
189 if(sBufferSubDataCompat
)
191 auto iter
= std::find(mExtensions
.begin(), mExtensions
.end(), "AL_EXT_SOURCE_RADIUS"sv
);
192 if(iter
!= mExtensions
.end()) mExtensions
.erase(iter
);
193 /* TODO: Would be nice to sort this alphabetically. Needs case-
194 * insensitive searching.
196 mExtensions
.emplace_back("AL_SOFT_buffer_sub_data"sv
);
200 eax_initialize_extensions();
203 if(!mExtensions
.empty())
205 const size_t len
{std::accumulate(mExtensions
.cbegin()+1, mExtensions
.cend(),
206 mExtensions
.front().length(),
207 [](size_t current
, std::string_view ext
) noexcept
208 { return current
+ ext
.length() + 1; })};
210 std::string extensions
;
211 extensions
.reserve(len
);
212 extensions
+= mExtensions
.front();
213 for(std::string_view ext
: al::span
{mExtensions
}.subspan
<1>())
219 mExtensionsString
= std::move(extensions
);
226 mParams
.Position
= alu::Vector
{0.0f
, 0.0f
, 0.0f
, 1.0f
};
227 mParams
.Matrix
= alu::Matrix::Identity();
228 mParams
.Velocity
= alu::Vector
{};
229 mParams
.Gain
= mListener
.Gain
;
230 mParams
.MetersPerUnit
= mListener
.mMetersPerUnit
232 * eaxGetDistanceFactor()
235 mParams
.AirAbsorptionGainHF
= mAirAbsorptionGainHF
;
236 mParams
.DopplerFactor
= mDopplerFactor
;
237 mParams
.SpeedOfSound
= mSpeedOfSound
* mDopplerVelocity
239 / eaxGetDistanceFactor()
242 mParams
.SourceDistanceModel
= mSourceDistanceModel
;
243 mParams
.mDistanceModel
= mDistanceModel
;
246 mAsyncEvents
= RingBuffer::Create(1024, sizeof(AsyncEvent
), false);
247 StartEventThrd(this);
251 mActiveVoiceCount
.store(64, std::memory_order_relaxed
);
254 void ALCcontext::deinit()
256 if(sLocalContext
== this)
258 WARN("%p released while current on thread\n", voidp
{this});
259 sThreadContext
.set(nullptr);
260 const auto rc
[[maybe_unused
]] = dec_ref();
264 ALCcontext
*origctx
{this};
265 if(sGlobalContext
.compare_exchange_strong(origctx
, nullptr))
267 while(sGlobalContextLock
.load()) {
268 /* Wait to make sure another thread didn't get the context and is
269 * trying to increment its refcount.
272 const auto rc
[[maybe_unused
]] = dec_ref();
277 /* First make sure this context exists in the device's list. */
278 auto *oldarray
= mDevice
->mContexts
.load(std::memory_order_acquire
);
279 if(auto toremove
= static_cast<size_t>(std::count(oldarray
->begin(), oldarray
->end(), this)))
281 using ContextArray
= al::FlexArray
<ContextBase
*>;
282 const size_t newsize
{oldarray
->size() - toremove
};
283 auto newarray
= ContextArray::Create(newsize
);
285 /* Copy the current/old context handles to the new array, excluding the
288 std::copy_if(oldarray
->begin(), oldarray
->end(), newarray
->begin(),
289 [this](ContextBase
*ctx
) { return ctx
!= this; });
291 /* Store the new context array in the device. Wait for any current mix
292 * to finish before deleting the old array.
294 auto prevarray
= mDevice
->mContexts
.exchange(std::move(newarray
));
295 std::ignore
= mDevice
->waitForMix();
297 stopPlayback
= (newsize
== 0);
300 stopPlayback
= oldarray
->empty();
304 if(stopPlayback
&& mALDevice
->mDeviceState
== DeviceState::Playing
)
306 mALDevice
->Backend
->stop();
307 mALDevice
->mDeviceState
= DeviceState::Configured
;
311 void ALCcontext::applyAllUpdates()
313 /* Tell the mixer to stop applying updates, then wait for any active
314 * updating to finish, before providing updates.
316 mHoldUpdates
.store(true, std::memory_order_release
);
317 while((mUpdateCount
.load(std::memory_order_acquire
)&1) != 0) {
326 if(std::exchange(mPropsDirty
, false))
327 UpdateContextProps(this);
328 UpdateAllEffectSlotProps(this);
329 UpdateAllSourceProps(this);
331 /* Now with all updates declared, let the mixer continue applying them so
332 * they all happen at once.
334 mHoldUpdates
.store(false, std::memory_order_release
);
342 void ForEachSource(ALCcontext
*context
, F func
)
344 for(auto &sublist
: context
->mSourceList
)
346 uint64_t usemask
{~sublist
.FreeMask
};
349 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
350 usemask
&= ~(1_u64
<< idx
);
352 func((*sublist
.Sources
)[idx
]);
360 bool ALCcontext::eaxIsCapable() const noexcept
362 return eax_has_enough_aux_sends();
365 void ALCcontext::eaxUninitialize() noexcept
367 if(!mEaxIsInitialized
)
370 mEaxIsInitialized
= false;
372 mEaxFxSlots
.uninitialize();
375 ALenum
ALCcontext::eax_eax_set(
376 const GUID
* property_set_id
,
378 ALuint property_source_id
,
379 ALvoid
* property_value
,
380 ALuint property_value_size
)
382 const auto call
= create_eax_call(
388 property_value_size
);
392 switch(call
.get_property_set_id())
394 case EaxCallPropertySetId::context
:
397 case EaxCallPropertySetId::fx_slot
:
398 case EaxCallPropertySetId::fx_slot_effect
:
399 eax_dispatch_fx_slot(call
);
401 case EaxCallPropertySetId::source
:
402 eax_dispatch_source(call
);
405 eax_fail_unknown_property_set_id();
407 mEaxNeedsCommit
= true;
409 if(!call
.is_deferred())
419 ALenum
ALCcontext::eax_eax_get(
420 const GUID
* property_set_id
,
422 ALuint property_source_id
,
423 ALvoid
* property_value
,
424 ALuint property_value_size
)
426 const auto call
= create_eax_call(
432 property_value_size
);
436 switch(call
.get_property_set_id())
438 case EaxCallPropertySetId::context
:
441 case EaxCallPropertySetId::fx_slot
:
442 case EaxCallPropertySetId::fx_slot_effect
:
443 eax_dispatch_fx_slot(call
);
445 case EaxCallPropertySetId::source
:
446 eax_dispatch_source(call
);
449 eax_fail_unknown_property_set_id();
455 void ALCcontext::eaxSetLastError() noexcept
457 mEaxLastError
= EAXERR_INVALID_OPERATION
;
460 [[noreturn
]] void ALCcontext::eax_fail(const char* message
)
462 throw ContextException
{message
};
465 [[noreturn
]] void ALCcontext::eax_fail_unknown_property_set_id()
467 eax_fail("Unknown property ID.");
470 [[noreturn
]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
472 eax_fail("Unknown primary FX Slot ID.");
475 [[noreturn
]] void ALCcontext::eax_fail_unknown_property_id()
477 eax_fail("Unknown property ID.");
480 [[noreturn
]] void ALCcontext::eax_fail_unknown_version()
482 eax_fail("Unknown version.");
485 void ALCcontext::eax_initialize_extensions()
487 if(!eax_g_is_enabled
)
490 mExtensions
.emplace(mExtensions
.begin(), "EAX-RAM"sv
);
493 mExtensions
.emplace(mExtensions
.begin(), "EAX5.0"sv
);
494 mExtensions
.emplace(mExtensions
.begin(), "EAX4.0"sv
);
495 mExtensions
.emplace(mExtensions
.begin(), "EAX3.0"sv
);
496 mExtensions
.emplace(mExtensions
.begin(), "EAX2.0"sv
);
497 mExtensions
.emplace(mExtensions
.begin(), "EAX"sv
);
501 void ALCcontext::eax_initialize()
503 if(mEaxIsInitialized
)
511 if(!eax_g_is_enabled
)
512 eax_fail("EAX disabled by a configuration.");
514 eax_ensure_compatibility();
516 eax_context_commit_air_absorbtion_hf();
517 eax_update_speaker_configuration();
518 eax_initialize_fx_slots();
520 mEaxIsInitialized
= true;
523 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
525 return mDefaultSlot
== nullptr;
528 void ALCcontext::eax_ensure_no_default_effect_slot() const
530 if(!eax_has_no_default_effect_slot())
531 eax_fail("There is a default effect slot in the context.");
534 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
536 return mALDevice
->NumAuxSends
>= EAX_MAX_FXSLOTS
;
539 void ALCcontext::eax_ensure_enough_aux_sends() const
541 if(!eax_has_enough_aux_sends())
542 eax_fail("Not enough aux sends.");
545 void ALCcontext::eax_ensure_compatibility()
547 eax_ensure_enough_aux_sends();
550 unsigned long ALCcontext::eax_detect_speaker_configuration() const
552 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
554 switch(mDevice
->FmtChans
)
556 case DevFmtMono
: return SPEAKERS_2
;
558 /* Pretend 7.1 if using UHJ output, since they both provide full
559 * horizontal surround.
561 if(mDevice
->mUhjEncoder
)
563 if(mDevice
->Flags
.test(DirectEar
))
566 case DevFmtQuad
: return SPEAKERS_4
;
567 case DevFmtX51
: return SPEAKERS_5
;
568 case DevFmtX61
: return SPEAKERS_6
;
569 case DevFmtX71
: return SPEAKERS_7
;
570 /* 7.1.4(.4) is compatible with 7.1. This could instead be HEADPHONES to
571 * suggest with-height surround sound (like HRTF).
573 case DevFmtX714
: return SPEAKERS_7
;
574 case DevFmtX7144
: return SPEAKERS_7
;
575 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
576 * suggest full-sphere surround sound (like HRTF).
578 case DevFmtX3D71
: return SPEAKERS_5
;
579 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
580 * provide full-sphere surround sound. Depends if apps are more likely to
581 * consider headphones or 7.1 for surround sound support.
583 case DevFmtAmbi3D
: return SPEAKERS_7
;
585 ERR(EAX_PREFIX
"Unexpected device channel format 0x%x.\n", mDevice
->FmtChans
);
591 void ALCcontext::eax_update_speaker_configuration()
593 mEaxSpeakerConfig
= eax_detect_speaker_configuration();
596 void ALCcontext::eax_set_last_error_defaults() noexcept
598 mEaxLastError
= EAXCONTEXT_DEFAULTLASTERROR
;
601 void ALCcontext::eax_session_set_defaults() noexcept
603 mEaxSession
.ulEAXVersion
= EAXCONTEXT_DEFAULTEAXSESSION
;
604 mEaxSession
.ulMaxActiveSends
= EAXCONTEXT_DEFAULTMAXACTIVESENDS
;
607 void ALCcontext::eax4_context_set_defaults(Eax4Props
& props
) noexcept
609 props
.guidPrimaryFXSlotID
= EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID
;
610 props
.flDistanceFactor
= EAXCONTEXT_DEFAULTDISTANCEFACTOR
;
611 props
.flAirAbsorptionHF
= EAXCONTEXT_DEFAULTAIRABSORPTIONHF
;
612 props
.flHFReference
= EAXCONTEXT_DEFAULTHFREFERENCE
;
615 void ALCcontext::eax4_context_set_defaults(Eax4State
& state
) noexcept
617 eax4_context_set_defaults(state
.i
);
621 void ALCcontext::eax5_context_set_defaults(Eax5Props
& props
) noexcept
623 props
.guidPrimaryFXSlotID
= EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID
;
624 props
.flDistanceFactor
= EAXCONTEXT_DEFAULTDISTANCEFACTOR
;
625 props
.flAirAbsorptionHF
= EAXCONTEXT_DEFAULTAIRABSORPTIONHF
;
626 props
.flHFReference
= EAXCONTEXT_DEFAULTHFREFERENCE
;
627 props
.flMacroFXFactor
= EAXCONTEXT_DEFAULTMACROFXFACTOR
;
630 void ALCcontext::eax5_context_set_defaults(Eax5State
& state
) noexcept
632 eax5_context_set_defaults(state
.i
);
636 void ALCcontext::eax_context_set_defaults()
638 eax5_context_set_defaults(mEax123
);
639 eax4_context_set_defaults(mEax4
);
640 eax5_context_set_defaults(mEax5
);
643 mEaxDf
= EaxDirtyFlags
{};
646 void ALCcontext::eax_set_defaults()
648 eax_set_last_error_defaults();
649 eax_session_set_defaults();
650 eax_context_set_defaults();
653 void ALCcontext::eax_dispatch_fx_slot(const EaxCall
& call
)
655 const auto fx_slot_index
= call
.get_fx_slot_index();
656 if(!fx_slot_index
.has_value())
657 eax_fail("Invalid fx slot index.");
659 auto& fx_slot
= eaxGetFxSlot(*fx_slot_index
);
660 if(fx_slot
.eax_dispatch(call
))
662 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
663 ForEachSource(this, std::mem_fn(&ALsource::eaxMarkAsChanged
));
667 void ALCcontext::eax_dispatch_source(const EaxCall
& call
)
669 const auto source_id
= call
.get_property_al_name();
670 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
671 const auto source
= ALsource::EaxLookupSource(*this, source_id
);
673 if (source
== nullptr)
674 eax_fail("Source not found.");
676 source
->eaxDispatch(call
);
679 void ALCcontext::eax_get_misc(const EaxCall
& call
)
681 switch(call
.get_property_id())
683 case EAXCONTEXT_NONE
:
685 case EAXCONTEXT_LASTERROR
:
686 call
.set_value
<ContextException
>(mEaxLastError
);
687 mEaxLastError
= EAX_OK
;
689 case EAXCONTEXT_SPEAKERCONFIG
:
690 call
.set_value
<ContextException
>(mEaxSpeakerConfig
);
692 case EAXCONTEXT_EAXSESSION
:
693 call
.set_value
<ContextException
>(mEaxSession
);
696 eax_fail_unknown_property_id();
700 void ALCcontext::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
702 switch(call
.get_property_id())
704 case EAXCONTEXT_ALLPARAMETERS
:
705 call
.set_value
<ContextException
>(props
);
707 case EAXCONTEXT_PRIMARYFXSLOTID
:
708 call
.set_value
<ContextException
>(props
.guidPrimaryFXSlotID
);
710 case EAXCONTEXT_DISTANCEFACTOR
:
711 call
.set_value
<ContextException
>(props
.flDistanceFactor
);
713 case EAXCONTEXT_AIRABSORPTIONHF
:
714 call
.set_value
<ContextException
>(props
.flAirAbsorptionHF
);
716 case EAXCONTEXT_HFREFERENCE
:
717 call
.set_value
<ContextException
>(props
.flHFReference
);
725 void ALCcontext::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
727 switch(call
.get_property_id())
729 case EAXCONTEXT_ALLPARAMETERS
:
730 call
.set_value
<ContextException
>(props
);
732 case EAXCONTEXT_PRIMARYFXSLOTID
:
733 call
.set_value
<ContextException
>(props
.guidPrimaryFXSlotID
);
735 case EAXCONTEXT_DISTANCEFACTOR
:
736 call
.set_value
<ContextException
>(props
.flDistanceFactor
);
738 case EAXCONTEXT_AIRABSORPTIONHF
:
739 call
.set_value
<ContextException
>(props
.flAirAbsorptionHF
);
741 case EAXCONTEXT_HFREFERENCE
:
742 call
.set_value
<ContextException
>(props
.flHFReference
);
744 case EAXCONTEXT_MACROFXFACTOR
:
745 call
.set_value
<ContextException
>(props
.flMacroFXFactor
);
753 void ALCcontext::eax_get(const EaxCall
& call
)
755 switch(call
.get_version())
757 case 4: eax4_get(call
, mEax4
.i
); break;
758 case 5: eax5_get(call
, mEax5
.i
); break;
759 default: eax_fail_unknown_version();
763 void ALCcontext::eax_context_commit_primary_fx_slot_id()
765 mEaxPrimaryFxSlotIndex
= mEax
.guidPrimaryFXSlotID
;
768 void ALCcontext::eax_context_commit_distance_factor()
770 /* mEax.flDistanceFactor was changed, so the context props are dirty. */
774 void ALCcontext::eax_context_commit_air_absorbtion_hf()
776 const auto new_value
= level_mb_to_gain(mEax
.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 mEaxFxSlots
.initialize(*this);
798 mEaxPrimaryFxSlotIndex
= mEax
.guidPrimaryFXSlotID
;
801 void ALCcontext::eax_update_sources()
803 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
804 auto update_source
= [](ALsource
&source
)
805 { source
.eaxCommit(); };
806 ForEachSource(this, update_source
);
809 void ALCcontext::eax_set_misc(const EaxCall
& call
)
811 switch(call
.get_property_id())
813 case EAXCONTEXT_NONE
:
815 case EAXCONTEXT_SPEAKERCONFIG
:
816 eax_set
<Eax5SpeakerConfigValidator
>(call
, mEaxSpeakerConfig
);
818 case EAXCONTEXT_EAXSESSION
:
819 eax_set
<Eax5SessionAllValidator
>(call
, mEaxSession
);
822 eax_fail_unknown_property_id();
826 void ALCcontext::eax4_defer_all(const EaxCall
& call
, Eax4State
& state
)
828 const auto& src
= call
.get_value
<ContextException
, const EAX40CONTEXTPROPERTIES
>();
829 Eax4AllValidator
{}(src
);
830 const auto& dst_i
= state
.i
;
831 auto& dst_d
= state
.d
;
834 if(dst_i
.guidPrimaryFXSlotID
!= dst_d
.guidPrimaryFXSlotID
)
835 mEaxDf
|= eax_primary_fx_slot_id_dirty_bit
;
837 if(dst_i
.flDistanceFactor
!= dst_d
.flDistanceFactor
)
838 mEaxDf
|= eax_distance_factor_dirty_bit
;
840 if(dst_i
.flAirAbsorptionHF
!= dst_d
.flAirAbsorptionHF
)
841 mEaxDf
|= eax_air_absorption_hf_dirty_bit
;
843 if(dst_i
.flHFReference
!= dst_d
.flHFReference
)
844 mEaxDf
|= eax_hf_reference_dirty_bit
;
847 void ALCcontext::eax4_defer(const EaxCall
& call
, Eax4State
& state
)
849 switch(call
.get_property_id())
851 case EAXCONTEXT_ALLPARAMETERS
:
852 eax4_defer_all(call
, state
);
854 case EAXCONTEXT_PRIMARYFXSLOTID
:
855 eax_defer
<Eax4PrimaryFxSlotIdValidator
, eax_primary_fx_slot_id_dirty_bit
>(
856 call
, state
, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
858 case EAXCONTEXT_DISTANCEFACTOR
:
859 eax_defer
<Eax4DistanceFactorValidator
, eax_distance_factor_dirty_bit
>(
860 call
, state
, &EAX40CONTEXTPROPERTIES::flDistanceFactor
);
862 case EAXCONTEXT_AIRABSORPTIONHF
:
863 eax_defer
<Eax4AirAbsorptionHfValidator
, eax_air_absorption_hf_dirty_bit
>(
864 call
, state
, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF
);
866 case EAXCONTEXT_HFREFERENCE
:
867 eax_defer
<Eax4HfReferenceValidator
, eax_hf_reference_dirty_bit
>(
868 call
, state
, &EAX40CONTEXTPROPERTIES::flHFReference
);
876 void ALCcontext::eax5_defer_all(const EaxCall
& call
, Eax5State
& state
)
878 const auto& src
= call
.get_value
<ContextException
, const EAX50CONTEXTPROPERTIES
>();
879 Eax4AllValidator
{}(src
);
880 const auto& dst_i
= state
.i
;
881 auto& dst_d
= state
.d
;
884 if(dst_i
.guidPrimaryFXSlotID
!= dst_d
.guidPrimaryFXSlotID
)
885 mEaxDf
|= eax_primary_fx_slot_id_dirty_bit
;
887 if(dst_i
.flDistanceFactor
!= dst_d
.flDistanceFactor
)
888 mEaxDf
|= eax_distance_factor_dirty_bit
;
890 if(dst_i
.flAirAbsorptionHF
!= dst_d
.flAirAbsorptionHF
)
891 mEaxDf
|= eax_air_absorption_hf_dirty_bit
;
893 if(dst_i
.flHFReference
!= dst_d
.flHFReference
)
894 mEaxDf
|= eax_hf_reference_dirty_bit
;
896 if(dst_i
.flMacroFXFactor
!= dst_d
.flMacroFXFactor
)
897 mEaxDf
|= eax_macro_fx_factor_dirty_bit
;
900 void ALCcontext::eax5_defer(const EaxCall
& call
, Eax5State
& state
)
902 switch(call
.get_property_id())
904 case EAXCONTEXT_ALLPARAMETERS
:
905 eax5_defer_all(call
, state
);
907 case EAXCONTEXT_PRIMARYFXSLOTID
:
908 eax_defer
<Eax5PrimaryFxSlotIdValidator
, eax_primary_fx_slot_id_dirty_bit
>(
909 call
, state
, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
911 case EAXCONTEXT_DISTANCEFACTOR
:
912 eax_defer
<Eax4DistanceFactorValidator
, eax_distance_factor_dirty_bit
>(
913 call
, state
, &EAX50CONTEXTPROPERTIES::flDistanceFactor
);
915 case EAXCONTEXT_AIRABSORPTIONHF
:
916 eax_defer
<Eax4AirAbsorptionHfValidator
, eax_air_absorption_hf_dirty_bit
>(
917 call
, state
, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF
);
919 case EAXCONTEXT_HFREFERENCE
:
920 eax_defer
<Eax4HfReferenceValidator
, eax_hf_reference_dirty_bit
>(
921 call
, state
, &EAX50CONTEXTPROPERTIES::flHFReference
);
923 case EAXCONTEXT_MACROFXFACTOR
:
924 eax_defer
<Eax5MacroFxFactorValidator
, eax_macro_fx_factor_dirty_bit
>(
925 call
, state
, &EAX50CONTEXTPROPERTIES::flMacroFXFactor
);
933 void ALCcontext::eax_set(const EaxCall
& call
)
935 const auto version
= call
.get_version();
938 case 4: eax4_defer(call
, mEax4
); break;
939 case 5: eax5_defer(call
, mEax5
); break;
940 default: eax_fail_unknown_version();
942 if(version
!= mEaxVersion
)
943 mEaxDf
= ~EaxDirtyFlags();
944 mEaxVersion
= version
;
947 void ALCcontext::eax4_context_commit(Eax4State
& state
, EaxDirtyFlags
& dst_df
)
949 if(mEaxDf
== EaxDirtyFlags
{})
952 eax_context_commit_property
<eax_primary_fx_slot_id_dirty_bit
>(
953 state
, dst_df
, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
954 eax_context_commit_property
<eax_distance_factor_dirty_bit
>(
955 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flDistanceFactor
);
956 eax_context_commit_property
<eax_air_absorption_hf_dirty_bit
>(
957 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF
);
958 eax_context_commit_property
<eax_hf_reference_dirty_bit
>(
959 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flHFReference
);
961 mEaxDf
= EaxDirtyFlags
{};
964 void ALCcontext::eax5_context_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
)
966 if(mEaxDf
== EaxDirtyFlags
{})
969 eax_context_commit_property
<eax_primary_fx_slot_id_dirty_bit
>(
970 state
, dst_df
, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
971 eax_context_commit_property
<eax_distance_factor_dirty_bit
>(
972 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flDistanceFactor
);
973 eax_context_commit_property
<eax_air_absorption_hf_dirty_bit
>(
974 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF
);
975 eax_context_commit_property
<eax_hf_reference_dirty_bit
>(
976 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flHFReference
);
977 eax_context_commit_property
<eax_macro_fx_factor_dirty_bit
>(
978 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flMacroFXFactor
);
980 mEaxDf
= EaxDirtyFlags
{};
983 void ALCcontext::eax_context_commit()
985 auto dst_df
= EaxDirtyFlags
{};
992 eax5_context_commit(mEax123
, dst_df
);
995 eax4_context_commit(mEax4
, dst_df
);
998 eax5_context_commit(mEax5
, dst_df
);
1002 if(dst_df
== EaxDirtyFlags
{})
1005 if((dst_df
& eax_primary_fx_slot_id_dirty_bit
) != EaxDirtyFlags
{})
1006 eax_context_commit_primary_fx_slot_id();
1008 if((dst_df
& eax_distance_factor_dirty_bit
) != EaxDirtyFlags
{})
1009 eax_context_commit_distance_factor();
1011 if((dst_df
& eax_air_absorption_hf_dirty_bit
) != EaxDirtyFlags
{})
1012 eax_context_commit_air_absorbtion_hf();
1014 if((dst_df
& eax_hf_reference_dirty_bit
) != EaxDirtyFlags
{})
1015 eax_context_commit_hf_reference();
1017 if((dst_df
& eax_macro_fx_factor_dirty_bit
) != EaxDirtyFlags
{})
1018 eax_context_commit_macro_fx_factor();
1020 if((dst_df
& eax_primary_fx_slot_id_dirty_bit
) != EaxDirtyFlags
{})
1021 eax_update_sources();
1024 void ALCcontext::eaxCommit()
1026 mEaxNeedsCommit
= false;
1027 eax_context_commit();
1029 eax_update_sources();
1033 FORCE_ALIGN
auto AL_APIENTRY
EAXSet(const GUID
*property_set_id
, ALuint property_id
,
1034 ALuint source_id
, ALvoid
*value
, ALuint value_size
) noexcept
-> ALenum
1036 auto context
= GetContextRef();
1037 if(!context
) UNLIKELY
return AL_INVALID_OPERATION
;
1038 return EAXSetDirect(context
.get(), property_set_id
, property_id
, source_id
, value
, value_size
);
1041 FORCE_ALIGN
auto AL_APIENTRY
EAXSetDirect(ALCcontext
*context
, const GUID
*property_set_id
,
1042 ALuint property_id
, ALuint source_id
, ALvoid
*value
, ALuint value_size
) noexcept
-> ALenum
1045 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1046 return context
->eax_eax_set(property_set_id
, property_id
, source_id
, value
, value_size
);
1050 context
->eaxSetLastError();
1051 eax_log_exception(std::data(__func__
));
1052 return AL_INVALID_OPERATION
;
1056 FORCE_ALIGN
auto AL_APIENTRY
EAXGet(const GUID
*property_set_id
, ALuint property_id
,
1057 ALuint source_id
, ALvoid
*value
, ALuint value_size
) noexcept
-> ALenum
1059 auto context
= GetContextRef();
1060 if(!context
) UNLIKELY
return AL_INVALID_OPERATION
;
1061 return EAXGetDirect(context
.get(), property_set_id
, property_id
, source_id
, value
, value_size
);
1064 FORCE_ALIGN
auto AL_APIENTRY
EAXGetDirect(ALCcontext
*context
, const GUID
*property_set_id
,
1065 ALuint property_id
, ALuint source_id
, ALvoid
*value
, ALuint value_size
) noexcept
-> ALenum
1068 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1069 return context
->eax_eax_get(property_set_id
, property_id
, source_id
, value
, value_size
);
1073 context
->eaxSetLastError();
1074 eax_log_exception(std::data(__func__
));
1075 return AL_INVALID_OPERATION
;
1077 #endif // ALSOFT_EAX