14 #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"
29 #include "core/async_event.h"
30 #include "core/device.h"
31 #include "core/effectslot.h"
32 #include "core/logging.h"
33 #include "core/voice.h"
34 #include "core/voice_change.h"
36 #include "flexarray.h"
37 #include "ringbuffer.h"
42 #include "al/eax/globals.h"
47 using namespace std::string_view_literals
;
50 /* Default context extensions */
51 std::vector
<std::string_view
> getContextExtensions() noexcept
53 return std::vector
<std::string_view
>{
57 "AL_EXT_direct_context"sv
,
59 "AL_EXT_EXPONENT_DISTANCE"sv
,
62 "AL_EXT_LINEAR_DISTANCE"sv
,
65 "AL_EXT_MULAW_BFORMAT"sv
,
66 "AL_EXT_MULAW_MCFORMATS"sv
,
68 "AL_EXT_source_distance_model"sv
,
69 "AL_EXT_SOURCE_RADIUS"sv
,
70 "AL_EXT_STATIC_BUFFER"sv
,
71 "AL_EXT_STEREO_ANGLES"sv
,
72 "AL_LOKI_quadriphonic"sv
,
73 "AL_SOFT_bformat_ex"sv
,
74 "AL_SOFTX_bformat_hoa"sv
,
75 "AL_SOFT_block_alignment"sv
,
76 "AL_SOFT_buffer_length_query"sv
,
77 "AL_SOFT_callback_buffer"sv
,
78 "AL_SOFTX_convolution_effect"sv
,
79 "AL_SOFT_deferred_updates"sv
,
80 "AL_SOFT_direct_channels"sv
,
81 "AL_SOFT_direct_channels_remix"sv
,
82 "AL_SOFT_effect_target"sv
,
84 "AL_SOFT_gain_clamp_ex"sv
,
85 "AL_SOFTX_hold_on_disconnect"sv
,
86 "AL_SOFT_loop_points"sv
,
87 "AL_SOFTX_map_buffer"sv
,
89 "AL_SOFT_source_latency"sv
,
90 "AL_SOFT_source_length"sv
,
91 "AL_SOFTX_source_panning"sv
,
92 "AL_SOFT_source_resampler"sv
,
93 "AL_SOFT_source_spatialize"sv
,
94 "AL_SOFT_source_start_delay"sv
,
103 std::atomic
<bool> ALCcontext::sGlobalContextLock
{false};
104 std::atomic
<ALCcontext
*> ALCcontext::sGlobalContext
{nullptr};
106 ALCcontext::ThreadCtx::~ThreadCtx()
108 if(ALCcontext
*ctx
{std::exchange(ALCcontext::sLocalContext
, nullptr)})
110 const bool result
{ctx
->releaseIfNoDelete()};
111 ERR("Context %p current for thread being destroyed%s!\n", voidp
{ctx
},
112 result
? "" : ", leak detected");
115 thread_local
ALCcontext::ThreadCtx
ALCcontext::sThreadContext
;
117 ALeffect
ALCcontext::sDefaultEffect
;
120 ALCcontext::ALCcontext(al::intrusive_ptr
<ALCdevice
> device
, ContextFlagBitset flags
)
121 : ContextBase
{device
.get()}, mALDevice
{std::move(device
)}, mContextFlags
{flags
}
123 mDebugGroups
.emplace_back(DebugSource::Other
, 0, std::string
{});
124 mDebugEnabled
.store(mContextFlags
.test(ContextFlags::DebugBit
), std::memory_order_relaxed
);
127 ALCcontext::~ALCcontext()
129 TRACE("Freeing context %p\n", voidp
{this});
131 size_t count
{std::accumulate(mSourceList
.cbegin(), mSourceList
.cend(), 0_uz
,
132 [](size_t cur
, const SourceSubList
&sublist
) noexcept
-> size_t
133 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); })};
135 WARN("%zu Source%s not deleted\n", count
, (count
==1)?"":"s");
143 mDefaultSlot
= nullptr;
144 count
= std::accumulate(mEffectSlotList
.cbegin(), mEffectSlotList
.cend(), 0_uz
,
145 [](size_t cur
, const EffectSlotSubList
&sublist
) noexcept
-> size_t
146 { return cur
+ static_cast<uint
>(al::popcount(~sublist
.FreeMask
)); });
148 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count
, (count
==1)?"":"s");
149 mEffectSlotList
.clear();
153 void ALCcontext::init()
155 if(sDefaultEffect
.type
!= AL_EFFECT_NULL
&& mDevice
->Type
== DeviceType::Playback
)
157 mDefaultSlot
= std::make_unique
<ALeffectslot
>(this);
158 aluInitEffectPanning(mDefaultSlot
->mSlot
, this);
161 std::unique_ptr
<EffectSlotArray
> auxslots
;
163 auxslots
= EffectSlot::CreatePtrArray(0);
166 auxslots
= EffectSlot::CreatePtrArray(1);
167 (*auxslots
)[0] = mDefaultSlot
->mSlot
;
168 std::uninitialized_fill_n(al::to_address(auxslots
->end()), 1_uz
, nullptr);
169 mDefaultSlot
->mState
= SlotState::Playing
;
171 mActiveAuxSlots
.store(std::move(auxslots
), std::memory_order_relaxed
);
175 VoiceChange
*cur
{mVoiceChangeTail
};
176 while(VoiceChange
*next
{cur
->mNext
.load(std::memory_order_relaxed
)})
178 mCurrentVoiceChange
.store(cur
, std::memory_order_relaxed
);
181 mExtensions
= getContextExtensions();
183 if(sBufferSubDataCompat
)
185 auto iter
= std::find(mExtensions
.begin(), mExtensions
.end(), "AL_EXT_SOURCE_RADIUS");
186 if(iter
!= mExtensions
.end()) mExtensions
.erase(iter
);
187 /* TODO: Would be nice to sort this alphabetically. Needs case-
188 * insensitive searching.
190 mExtensions
.emplace_back("AL_SOFT_buffer_sub_data");
194 eax_initialize_extensions();
197 if(!mExtensions
.empty())
199 const size_t len
{std::accumulate(mExtensions
.cbegin()+1, mExtensions
.cend(),
200 mExtensions
.front().length(),
201 [](size_t current
, std::string_view ext
) noexcept
202 { return current
+ ext
.length() + 1; })};
204 std::string extensions
;
205 extensions
.reserve(len
);
206 extensions
+= mExtensions
.front();
207 for(std::string_view ext
: al::span
{mExtensions
}.subspan
<1>())
213 mExtensionsString
= std::move(extensions
);
216 mParams
.Position
= alu::Vector
{0.0f
, 0.0f
, 0.0f
, 1.0f
};
217 mParams
.Matrix
= alu::Matrix::Identity();
218 mParams
.Velocity
= alu::Vector
{};
219 mParams
.Gain
= mListener
.Gain
;
220 mParams
.MetersPerUnit
= mListener
.mMetersPerUnit
;
221 mParams
.AirAbsorptionGainHF
= mAirAbsorptionGainHF
;
222 mParams
.DopplerFactor
= mDopplerFactor
;
223 mParams
.SpeedOfSound
= mSpeedOfSound
* mDopplerVelocity
;
224 mParams
.SourceDistanceModel
= mSourceDistanceModel
;
225 mParams
.mDistanceModel
= mDistanceModel
;
228 mAsyncEvents
= RingBuffer::Create(511, sizeof(AsyncEvent
), false);
229 StartEventThrd(this);
233 mActiveVoiceCount
.store(64, std::memory_order_relaxed
);
236 void ALCcontext::deinit()
238 if(sLocalContext
== this)
240 WARN("%p released while current on thread\n", voidp
{this});
241 sThreadContext
.set(nullptr);
245 ALCcontext
*origctx
{this};
246 if(sGlobalContext
.compare_exchange_strong(origctx
, nullptr))
248 while(sGlobalContextLock
.load()) {
249 /* Wait to make sure another thread didn't get the context and is
250 * trying to increment its refcount.
257 /* First make sure this context exists in the device's list. */
258 auto *oldarray
= mDevice
->mContexts
.load(std::memory_order_acquire
);
259 if(auto toremove
= static_cast<size_t>(std::count(oldarray
->begin(), oldarray
->end(), this)))
261 using ContextArray
= al::FlexArray
<ContextBase
*>;
262 const size_t newsize
{oldarray
->size() - toremove
};
263 auto newarray
= ContextArray::Create(newsize
);
265 /* Copy the current/old context handles to the new array, excluding the
268 std::copy_if(oldarray
->begin(), oldarray
->end(), newarray
->begin(),
269 [this](ContextBase
*ctx
) { return ctx
!= this; });
271 /* Store the new context array in the device. Wait for any current mix
272 * to finish before deleting the old array.
274 auto prevarray
= mDevice
->mContexts
.exchange(std::move(newarray
));
275 std::ignore
= mDevice
->waitForMix();
277 stopPlayback
= (newsize
== 0);
280 stopPlayback
= oldarray
->empty();
284 if(stopPlayback
&& mALDevice
->mDeviceState
== DeviceState::Playing
)
286 mALDevice
->Backend
->stop();
287 mALDevice
->mDeviceState
= DeviceState::Configured
;
291 void ALCcontext::applyAllUpdates()
293 /* Tell the mixer to stop applying updates, then wait for any active
294 * updating to finish, before providing updates.
296 mHoldUpdates
.store(true, std::memory_order_release
);
297 while((mUpdateCount
.load(std::memory_order_acquire
)&1) != 0) {
306 if(std::exchange(mPropsDirty
, false))
307 UpdateContextProps(this);
308 UpdateAllEffectSlotProps(this);
309 UpdateAllSourceProps(this);
311 /* Now with all updates declared, let the mixer continue applying them so
312 * they all happen at once.
314 mHoldUpdates
.store(false, std::memory_order_release
);
322 void ForEachSource(ALCcontext
*context
, F func
)
324 for(auto &sublist
: context
->mSourceList
)
326 uint64_t usemask
{~sublist
.FreeMask
};
329 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
330 usemask
&= ~(1_u64
<< idx
);
332 func((*sublist
.Sources
)[idx
]);
340 bool ALCcontext::eaxIsCapable() const noexcept
342 return eax_has_enough_aux_sends();
345 void ALCcontext::eaxUninitialize() noexcept
347 if(!mEaxIsInitialized
)
350 mEaxIsInitialized
= false;
352 mEaxFxSlots
.uninitialize();
355 ALenum
ALCcontext::eax_eax_set(
356 const GUID
* property_set_id
,
358 ALuint property_source_id
,
359 ALvoid
* property_value
,
360 ALuint property_value_size
)
362 const auto call
= create_eax_call(
368 property_value_size
);
372 switch(call
.get_property_set_id())
374 case EaxCallPropertySetId::context
:
377 case EaxCallPropertySetId::fx_slot
:
378 case EaxCallPropertySetId::fx_slot_effect
:
379 eax_dispatch_fx_slot(call
);
381 case EaxCallPropertySetId::source
:
382 eax_dispatch_source(call
);
385 eax_fail_unknown_property_set_id();
387 mEaxNeedsCommit
= true;
389 if(!call
.is_deferred())
399 ALenum
ALCcontext::eax_eax_get(
400 const GUID
* property_set_id
,
402 ALuint property_source_id
,
403 ALvoid
* property_value
,
404 ALuint property_value_size
)
406 const auto call
= create_eax_call(
412 property_value_size
);
416 switch(call
.get_property_set_id())
418 case EaxCallPropertySetId::context
:
421 case EaxCallPropertySetId::fx_slot
:
422 case EaxCallPropertySetId::fx_slot_effect
:
423 eax_dispatch_fx_slot(call
);
425 case EaxCallPropertySetId::source
:
426 eax_dispatch_source(call
);
429 eax_fail_unknown_property_set_id();
435 void ALCcontext::eaxSetLastError() noexcept
437 mEaxLastError
= EAXERR_INVALID_OPERATION
;
440 [[noreturn
]] void ALCcontext::eax_fail(const char* message
)
442 throw ContextException
{message
};
445 [[noreturn
]] void ALCcontext::eax_fail_unknown_property_set_id()
447 eax_fail("Unknown property ID.");
450 [[noreturn
]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
452 eax_fail("Unknown primary FX Slot ID.");
455 [[noreturn
]] void ALCcontext::eax_fail_unknown_property_id()
457 eax_fail("Unknown property ID.");
460 [[noreturn
]] void ALCcontext::eax_fail_unknown_version()
462 eax_fail("Unknown version.");
465 void ALCcontext::eax_initialize_extensions()
467 if(!eax_g_is_enabled
)
470 mExtensions
.emplace(mExtensions
.begin(), "EAX-RAM"sv
);
473 mExtensions
.emplace(mExtensions
.begin(), "EAX5.0"sv
);
474 mExtensions
.emplace(mExtensions
.begin(), "EAX4.0"sv
);
475 mExtensions
.emplace(mExtensions
.begin(), "EAX3.0"sv
);
476 mExtensions
.emplace(mExtensions
.begin(), "EAX2.0"sv
);
477 mExtensions
.emplace(mExtensions
.begin(), "EAX"sv
);
481 void ALCcontext::eax_initialize()
483 if(mEaxIsInitialized
)
491 if(!eax_g_is_enabled
)
492 eax_fail("EAX disabled by a configuration.");
494 eax_ensure_compatibility();
496 eax_context_commit_air_absorbtion_hf();
497 eax_update_speaker_configuration();
498 eax_initialize_fx_slots();
500 mEaxIsInitialized
= true;
503 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
505 return mDefaultSlot
== nullptr;
508 void ALCcontext::eax_ensure_no_default_effect_slot() const
510 if(!eax_has_no_default_effect_slot())
511 eax_fail("There is a default effect slot in the context.");
514 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
516 return mALDevice
->NumAuxSends
>= EAX_MAX_FXSLOTS
;
519 void ALCcontext::eax_ensure_enough_aux_sends() const
521 if(!eax_has_enough_aux_sends())
522 eax_fail("Not enough aux sends.");
525 void ALCcontext::eax_ensure_compatibility()
527 eax_ensure_enough_aux_sends();
530 unsigned long ALCcontext::eax_detect_speaker_configuration() const
532 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
534 switch(mDevice
->FmtChans
)
536 case DevFmtMono
: return SPEAKERS_2
;
538 /* Pretend 7.1 if using UHJ output, since they both provide full
539 * horizontal surround.
541 if(mDevice
->mUhjEncoder
)
543 if(mDevice
->Flags
.test(DirectEar
))
546 case DevFmtQuad
: return SPEAKERS_4
;
547 case DevFmtX51
: return SPEAKERS_5
;
548 case DevFmtX61
: return SPEAKERS_6
;
549 case DevFmtX71
: return SPEAKERS_7
;
550 /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
551 * suggest with-height surround sound (like HRTF).
553 case DevFmtX714
: return SPEAKERS_7
;
554 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
555 * suggest full-sphere surround sound (like HRTF).
557 case DevFmtX3D71
: return SPEAKERS_5
;
558 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
559 * provide full-sphere surround sound. Depends if apps are more likely to
560 * consider headphones or 7.1 for surround sound support.
562 case DevFmtAmbi3D
: return SPEAKERS_7
;
564 ERR(EAX_PREFIX
"Unexpected device channel format 0x%x.\n", mDevice
->FmtChans
);
570 void ALCcontext::eax_update_speaker_configuration()
572 mEaxSpeakerConfig
= eax_detect_speaker_configuration();
575 void ALCcontext::eax_set_last_error_defaults() noexcept
577 mEaxLastError
= EAXCONTEXT_DEFAULTLASTERROR
;
580 void ALCcontext::eax_session_set_defaults() noexcept
582 mEaxSession
.ulEAXVersion
= EAXCONTEXT_DEFAULTEAXSESSION
;
583 mEaxSession
.ulMaxActiveSends
= EAXCONTEXT_DEFAULTMAXACTIVESENDS
;
586 void ALCcontext::eax4_context_set_defaults(Eax4Props
& props
) noexcept
588 props
.guidPrimaryFXSlotID
= EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID
;
589 props
.flDistanceFactor
= EAXCONTEXT_DEFAULTDISTANCEFACTOR
;
590 props
.flAirAbsorptionHF
= EAXCONTEXT_DEFAULTAIRABSORPTIONHF
;
591 props
.flHFReference
= EAXCONTEXT_DEFAULTHFREFERENCE
;
594 void ALCcontext::eax4_context_set_defaults(Eax4State
& state
) noexcept
596 eax4_context_set_defaults(state
.i
);
600 void ALCcontext::eax5_context_set_defaults(Eax5Props
& props
) noexcept
602 props
.guidPrimaryFXSlotID
= EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID
;
603 props
.flDistanceFactor
= EAXCONTEXT_DEFAULTDISTANCEFACTOR
;
604 props
.flAirAbsorptionHF
= EAXCONTEXT_DEFAULTAIRABSORPTIONHF
;
605 props
.flHFReference
= EAXCONTEXT_DEFAULTHFREFERENCE
;
606 props
.flMacroFXFactor
= EAXCONTEXT_DEFAULTMACROFXFACTOR
;
609 void ALCcontext::eax5_context_set_defaults(Eax5State
& state
) noexcept
611 eax5_context_set_defaults(state
.i
);
615 void ALCcontext::eax_context_set_defaults()
617 eax5_context_set_defaults(mEax123
);
618 eax4_context_set_defaults(mEax4
);
619 eax5_context_set_defaults(mEax5
);
622 mEaxDf
= EaxDirtyFlags
{};
625 void ALCcontext::eax_set_defaults()
627 eax_set_last_error_defaults();
628 eax_session_set_defaults();
629 eax_context_set_defaults();
632 void ALCcontext::eax_dispatch_fx_slot(const EaxCall
& call
)
634 const auto fx_slot_index
= call
.get_fx_slot_index();
635 if(!fx_slot_index
.has_value())
636 eax_fail("Invalid fx slot index.");
638 auto& fx_slot
= eaxGetFxSlot(*fx_slot_index
);
639 if(fx_slot
.eax_dispatch(call
))
641 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
642 ForEachSource(this, std::mem_fn(&ALsource::eaxMarkAsChanged
));
646 void ALCcontext::eax_dispatch_source(const EaxCall
& call
)
648 const auto source_id
= call
.get_property_al_name();
649 std::lock_guard
<std::mutex
> source_lock
{mSourceLock
};
650 const auto source
= ALsource::EaxLookupSource(*this, source_id
);
652 if (source
== nullptr)
653 eax_fail("Source not found.");
655 source
->eaxDispatch(call
);
658 void ALCcontext::eax_get_misc(const EaxCall
& call
)
660 switch(call
.get_property_id())
662 case EAXCONTEXT_NONE
:
664 case EAXCONTEXT_LASTERROR
:
665 call
.set_value
<ContextException
>(mEaxLastError
);
666 mEaxLastError
= EAX_OK
;
668 case EAXCONTEXT_SPEAKERCONFIG
:
669 call
.set_value
<ContextException
>(mEaxSpeakerConfig
);
671 case EAXCONTEXT_EAXSESSION
:
672 call
.set_value
<ContextException
>(mEaxSession
);
675 eax_fail_unknown_property_id();
679 void ALCcontext::eax4_get(const EaxCall
& call
, const Eax4Props
& props
)
681 switch(call
.get_property_id())
683 case EAXCONTEXT_ALLPARAMETERS
:
684 call
.set_value
<ContextException
>(props
);
686 case EAXCONTEXT_PRIMARYFXSLOTID
:
687 call
.set_value
<ContextException
>(props
.guidPrimaryFXSlotID
);
689 case EAXCONTEXT_DISTANCEFACTOR
:
690 call
.set_value
<ContextException
>(props
.flDistanceFactor
);
692 case EAXCONTEXT_AIRABSORPTIONHF
:
693 call
.set_value
<ContextException
>(props
.flAirAbsorptionHF
);
695 case EAXCONTEXT_HFREFERENCE
:
696 call
.set_value
<ContextException
>(props
.flHFReference
);
704 void ALCcontext::eax5_get(const EaxCall
& call
, const Eax5Props
& props
)
706 switch(call
.get_property_id())
708 case EAXCONTEXT_ALLPARAMETERS
:
709 call
.set_value
<ContextException
>(props
);
711 case EAXCONTEXT_PRIMARYFXSLOTID
:
712 call
.set_value
<ContextException
>(props
.guidPrimaryFXSlotID
);
714 case EAXCONTEXT_DISTANCEFACTOR
:
715 call
.set_value
<ContextException
>(props
.flDistanceFactor
);
717 case EAXCONTEXT_AIRABSORPTIONHF
:
718 call
.set_value
<ContextException
>(props
.flAirAbsorptionHF
);
720 case EAXCONTEXT_HFREFERENCE
:
721 call
.set_value
<ContextException
>(props
.flHFReference
);
723 case EAXCONTEXT_MACROFXFACTOR
:
724 call
.set_value
<ContextException
>(props
.flMacroFXFactor
);
732 void ALCcontext::eax_get(const EaxCall
& call
)
734 switch(call
.get_version())
736 case 4: eax4_get(call
, mEax4
.i
); break;
737 case 5: eax5_get(call
, mEax5
.i
); break;
738 default: eax_fail_unknown_version();
742 void ALCcontext::eax_context_commit_primary_fx_slot_id()
744 mEaxPrimaryFxSlotIndex
= mEax
.guidPrimaryFXSlotID
;
747 void ALCcontext::eax_context_commit_distance_factor()
749 if(mListener
.mMetersPerUnit
== mEax
.flDistanceFactor
)
752 mListener
.mMetersPerUnit
= mEax
.flDistanceFactor
;
756 void ALCcontext::eax_context_commit_air_absorbtion_hf()
758 const auto new_value
= level_mb_to_gain(mEax
.flAirAbsorptionHF
);
760 if(mAirAbsorptionGainHF
== new_value
)
763 mAirAbsorptionGainHF
= new_value
;
767 void ALCcontext::eax_context_commit_hf_reference()
772 void ALCcontext::eax_context_commit_macro_fx_factor()
777 void ALCcontext::eax_initialize_fx_slots()
779 mEaxFxSlots
.initialize(*this);
780 mEaxPrimaryFxSlotIndex
= mEax
.guidPrimaryFXSlotID
;
783 void ALCcontext::eax_update_sources()
785 std::unique_lock
<std::mutex
> source_lock
{mSourceLock
};
786 auto update_source
= [](ALsource
&source
)
787 { source
.eaxCommit(); };
788 ForEachSource(this, update_source
);
791 void ALCcontext::eax_set_misc(const EaxCall
& call
)
793 switch(call
.get_property_id())
795 case EAXCONTEXT_NONE
:
797 case EAXCONTEXT_SPEAKERCONFIG
:
798 eax_set
<Eax5SpeakerConfigValidator
>(call
, mEaxSpeakerConfig
);
800 case EAXCONTEXT_EAXSESSION
:
801 eax_set
<Eax5SessionAllValidator
>(call
, mEaxSession
);
804 eax_fail_unknown_property_id();
808 void ALCcontext::eax4_defer_all(const EaxCall
& call
, Eax4State
& state
)
810 const auto& src
= call
.get_value
<ContextException
, const EAX40CONTEXTPROPERTIES
>();
811 Eax4AllValidator
{}(src
);
812 const auto& dst_i
= state
.i
;
813 auto& dst_d
= state
.d
;
816 if(dst_i
.guidPrimaryFXSlotID
!= dst_d
.guidPrimaryFXSlotID
)
817 mEaxDf
|= eax_primary_fx_slot_id_dirty_bit
;
819 if(dst_i
.flDistanceFactor
!= dst_d
.flDistanceFactor
)
820 mEaxDf
|= eax_distance_factor_dirty_bit
;
822 if(dst_i
.flAirAbsorptionHF
!= dst_d
.flAirAbsorptionHF
)
823 mEaxDf
|= eax_air_absorption_hf_dirty_bit
;
825 if(dst_i
.flHFReference
!= dst_d
.flHFReference
)
826 mEaxDf
|= eax_hf_reference_dirty_bit
;
829 void ALCcontext::eax4_defer(const EaxCall
& call
, Eax4State
& state
)
831 switch(call
.get_property_id())
833 case EAXCONTEXT_ALLPARAMETERS
:
834 eax4_defer_all(call
, state
);
836 case EAXCONTEXT_PRIMARYFXSLOTID
:
837 eax_defer
<Eax4PrimaryFxSlotIdValidator
, eax_primary_fx_slot_id_dirty_bit
>(
838 call
, state
, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
840 case EAXCONTEXT_DISTANCEFACTOR
:
841 eax_defer
<Eax4DistanceFactorValidator
, eax_distance_factor_dirty_bit
>(
842 call
, state
, &EAX40CONTEXTPROPERTIES::flDistanceFactor
);
844 case EAXCONTEXT_AIRABSORPTIONHF
:
845 eax_defer
<Eax4AirAbsorptionHfValidator
, eax_air_absorption_hf_dirty_bit
>(
846 call
, state
, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF
);
848 case EAXCONTEXT_HFREFERENCE
:
849 eax_defer
<Eax4HfReferenceValidator
, eax_hf_reference_dirty_bit
>(
850 call
, state
, &EAX40CONTEXTPROPERTIES::flHFReference
);
858 void ALCcontext::eax5_defer_all(const EaxCall
& call
, Eax5State
& state
)
860 const auto& src
= call
.get_value
<ContextException
, const EAX50CONTEXTPROPERTIES
>();
861 Eax4AllValidator
{}(src
);
862 const auto& dst_i
= state
.i
;
863 auto& dst_d
= state
.d
;
866 if(dst_i
.guidPrimaryFXSlotID
!= dst_d
.guidPrimaryFXSlotID
)
867 mEaxDf
|= eax_primary_fx_slot_id_dirty_bit
;
869 if(dst_i
.flDistanceFactor
!= dst_d
.flDistanceFactor
)
870 mEaxDf
|= eax_distance_factor_dirty_bit
;
872 if(dst_i
.flAirAbsorptionHF
!= dst_d
.flAirAbsorptionHF
)
873 mEaxDf
|= eax_air_absorption_hf_dirty_bit
;
875 if(dst_i
.flHFReference
!= dst_d
.flHFReference
)
876 mEaxDf
|= eax_hf_reference_dirty_bit
;
878 if(dst_i
.flMacroFXFactor
!= dst_d
.flMacroFXFactor
)
879 mEaxDf
|= eax_macro_fx_factor_dirty_bit
;
882 void ALCcontext::eax5_defer(const EaxCall
& call
, Eax5State
& state
)
884 switch(call
.get_property_id())
886 case EAXCONTEXT_ALLPARAMETERS
:
887 eax5_defer_all(call
, state
);
889 case EAXCONTEXT_PRIMARYFXSLOTID
:
890 eax_defer
<Eax5PrimaryFxSlotIdValidator
, eax_primary_fx_slot_id_dirty_bit
>(
891 call
, state
, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
893 case EAXCONTEXT_DISTANCEFACTOR
:
894 eax_defer
<Eax4DistanceFactorValidator
, eax_distance_factor_dirty_bit
>(
895 call
, state
, &EAX50CONTEXTPROPERTIES::flDistanceFactor
);
897 case EAXCONTEXT_AIRABSORPTIONHF
:
898 eax_defer
<Eax4AirAbsorptionHfValidator
, eax_air_absorption_hf_dirty_bit
>(
899 call
, state
, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF
);
901 case EAXCONTEXT_HFREFERENCE
:
902 eax_defer
<Eax4HfReferenceValidator
, eax_hf_reference_dirty_bit
>(
903 call
, state
, &EAX50CONTEXTPROPERTIES::flHFReference
);
905 case EAXCONTEXT_MACROFXFACTOR
:
906 eax_defer
<Eax5MacroFxFactorValidator
, eax_macro_fx_factor_dirty_bit
>(
907 call
, state
, &EAX50CONTEXTPROPERTIES::flMacroFXFactor
);
915 void ALCcontext::eax_set(const EaxCall
& call
)
917 const auto version
= call
.get_version();
920 case 4: eax4_defer(call
, mEax4
); break;
921 case 5: eax5_defer(call
, mEax5
); break;
922 default: eax_fail_unknown_version();
924 if(version
!= mEaxVersion
)
925 mEaxDf
= ~EaxDirtyFlags();
926 mEaxVersion
= version
;
929 void ALCcontext::eax4_context_commit(Eax4State
& state
, EaxDirtyFlags
& dst_df
)
931 if(mEaxDf
== EaxDirtyFlags
{})
934 eax_context_commit_property
<eax_primary_fx_slot_id_dirty_bit
>(
935 state
, dst_df
, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
936 eax_context_commit_property
<eax_distance_factor_dirty_bit
>(
937 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flDistanceFactor
);
938 eax_context_commit_property
<eax_air_absorption_hf_dirty_bit
>(
939 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF
);
940 eax_context_commit_property
<eax_hf_reference_dirty_bit
>(
941 state
, dst_df
, &EAX40CONTEXTPROPERTIES::flHFReference
);
943 mEaxDf
= EaxDirtyFlags
{};
946 void ALCcontext::eax5_context_commit(Eax5State
& state
, EaxDirtyFlags
& dst_df
)
948 if(mEaxDf
== EaxDirtyFlags
{})
951 eax_context_commit_property
<eax_primary_fx_slot_id_dirty_bit
>(
952 state
, dst_df
, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID
);
953 eax_context_commit_property
<eax_distance_factor_dirty_bit
>(
954 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flDistanceFactor
);
955 eax_context_commit_property
<eax_air_absorption_hf_dirty_bit
>(
956 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF
);
957 eax_context_commit_property
<eax_hf_reference_dirty_bit
>(
958 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flHFReference
);
959 eax_context_commit_property
<eax_macro_fx_factor_dirty_bit
>(
960 state
, dst_df
, &EAX50CONTEXTPROPERTIES::flMacroFXFactor
);
962 mEaxDf
= EaxDirtyFlags
{};
965 void ALCcontext::eax_context_commit()
967 auto dst_df
= EaxDirtyFlags
{};
974 eax5_context_commit(mEax123
, dst_df
);
977 eax4_context_commit(mEax4
, dst_df
);
980 eax5_context_commit(mEax5
, dst_df
);
984 if(dst_df
== EaxDirtyFlags
{})
987 if((dst_df
& eax_primary_fx_slot_id_dirty_bit
) != EaxDirtyFlags
{})
988 eax_context_commit_primary_fx_slot_id();
990 if((dst_df
& eax_distance_factor_dirty_bit
) != EaxDirtyFlags
{})
991 eax_context_commit_distance_factor();
993 if((dst_df
& eax_air_absorption_hf_dirty_bit
) != EaxDirtyFlags
{})
994 eax_context_commit_air_absorbtion_hf();
996 if((dst_df
& eax_hf_reference_dirty_bit
) != EaxDirtyFlags
{})
997 eax_context_commit_hf_reference();
999 if((dst_df
& eax_macro_fx_factor_dirty_bit
) != EaxDirtyFlags
{})
1000 eax_context_commit_macro_fx_factor();
1002 if((dst_df
& eax_primary_fx_slot_id_dirty_bit
) != EaxDirtyFlags
{})
1003 eax_update_sources();
1006 void ALCcontext::eaxCommit()
1008 mEaxNeedsCommit
= false;
1009 eax_context_commit();
1011 eax_update_sources();
1015 FORCE_ALIGN ALenum AL_APIENTRY
EAXSet(const GUID
*a
, ALuint b
, ALuint c
, ALvoid
*d
, ALuint e
) noexcept
1017 auto context
= GetContextRef();
1018 if(!context
) UNLIKELY
return AL_INVALID_OPERATION
;
1019 return EAXSetDirect(context
.get(), a
, b
, c
, d
, e
);
1022 FORCE_ALIGN ALenum AL_APIENTRY
EAXSetDirect(ALCcontext
*context
, const GUID
*property_set_id
,
1023 ALuint property_id
, ALuint property_source_id
, ALvoid
*property_value
,
1024 ALuint property_value_size
) noexcept
1027 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1028 return context
->eax_eax_set(
1033 property_value_size
);
1037 context
->eaxSetLastError();
1038 eax_log_exception(std::data(__func__
));
1039 return AL_INVALID_OPERATION
;
1043 FORCE_ALIGN ALenum AL_APIENTRY
EAXGet(const GUID
*a
, ALuint b
, ALuint c
, ALvoid
*d
, ALuint e
) noexcept
1045 auto context
= GetContextRef();
1046 if(!context
) UNLIKELY
return AL_INVALID_OPERATION
;
1047 return EAXGetDirect(context
.get(), a
, b
, c
, d
, e
);
1050 FORCE_ALIGN ALenum AL_APIENTRY
EAXGetDirect(ALCcontext
*context
, const GUID
*property_set_id
,
1051 ALuint property_id
, ALuint property_source_id
, ALvoid
*property_value
,
1052 ALuint property_value_size
) noexcept
1055 std::lock_guard
<std::mutex
> prop_lock
{context
->mPropLock
};
1056 return context
->eax_eax_get(
1061 property_value_size
);
1065 context
->eaxSetLastError();
1066 eax_log_exception(std::data(__func__
));
1067 return AL_INVALID_OPERATION
;
1069 #endif // ALSOFT_EAX