Replace a couple loops with algorithms
[openal-soft.git] / alc / context.cpp
blob325e2f2282a45ce2587b7868e097b72c810ef439
2 #include "config.h"
4 #include "context.h"
6 #include <algorithm>
7 #include <array>
8 #include <cstddef>
9 #include <cstring>
10 #include <functional>
11 #include <limits>
12 #include <numeric>
13 #include <stdexcept>
14 #include <string_view>
15 #include <utility>
17 #include "AL/efx.h"
19 #include "al/auxeffectslot.h"
20 #include "al/debug.h"
21 #include "al/source.h"
22 #include "al/effect.h"
23 #include "al/event.h"
24 #include "al/listener.h"
25 #include "albit.h"
26 #include "alc/alu.h"
27 #include "alc/backends/base.h"
28 #include "alspan.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"
35 #include "device.h"
36 #include "flexarray.h"
37 #include "ringbuffer.h"
38 #include "vecmat.h"
40 #ifdef ALSOFT_EAX
41 #include "al/eax/globals.h"
42 #endif // ALSOFT_EAX
44 namespace {
46 using namespace std::string_view_literals;
47 using voidp = void*;
49 /* Default context extensions */
50 std::vector<std::string_view> getContextExtensions() noexcept
52 return std::vector<std::string_view>{
53 "AL_EXT_ALAW"sv,
54 "AL_EXT_BFORMAT"sv,
55 "AL_EXT_debug"sv,
56 "AL_EXT_direct_context"sv,
57 "AL_EXT_DOUBLE"sv,
58 "AL_EXT_EXPONENT_DISTANCE"sv,
59 "AL_EXT_FLOAT32"sv,
60 "AL_EXT_IMA4"sv,
61 "AL_EXT_LINEAR_DISTANCE"sv,
62 "AL_EXT_MCFORMATS"sv,
63 "AL_EXT_MULAW"sv,
64 "AL_EXT_MULAW_BFORMAT"sv,
65 "AL_EXT_MULAW_MCFORMATS"sv,
66 "AL_EXT_OFFSET"sv,
67 "AL_EXT_source_distance_model"sv,
68 "AL_EXT_SOURCE_RADIUS"sv,
69 "AL_EXT_STATIC_BUFFER"sv,
70 "AL_EXT_STEREO_ANGLES"sv,
71 "AL_LOKI_quadriphonic"sv,
72 "AL_SOFT_bformat_ex"sv,
73 "AL_SOFTX_bformat_hoa"sv,
74 "AL_SOFT_block_alignment"sv,
75 "AL_SOFT_buffer_length_query"sv,
76 "AL_SOFT_callback_buffer"sv,
77 "AL_SOFTX_convolution_effect"sv,
78 "AL_SOFT_deferred_updates"sv,
79 "AL_SOFT_direct_channels"sv,
80 "AL_SOFT_direct_channels_remix"sv,
81 "AL_SOFT_effect_target"sv,
82 "AL_SOFT_events"sv,
83 "AL_SOFT_gain_clamp_ex"sv,
84 "AL_SOFTX_hold_on_disconnect"sv,
85 "AL_SOFT_loop_points"sv,
86 "AL_SOFTX_map_buffer"sv,
87 "AL_SOFT_MSADPCM"sv,
88 "AL_SOFT_source_latency"sv,
89 "AL_SOFT_source_length"sv,
90 "AL_SOFTX_source_panning"sv,
91 "AL_SOFT_source_resampler"sv,
92 "AL_SOFT_source_spatialize"sv,
93 "AL_SOFT_source_start_delay"sv,
94 "AL_SOFT_UHJ"sv,
95 "AL_SOFT_UHJ_ex"sv,
99 } // namespace
102 std::atomic<bool> ALCcontext::sGlobalContextLock{false};
103 std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
105 ALCcontext::ThreadCtx::~ThreadCtx()
107 if(ALCcontext *ctx{std::exchange(ALCcontext::sLocalContext, nullptr)})
109 const bool result{ctx->releaseIfNoDelete()};
110 ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
111 result ? "" : ", leak detected");
114 thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
116 ALeffect ALCcontext::sDefaultEffect;
119 ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags)
120 : ContextBase{device.get()}, mALDevice{std::move(device)}, mContextFlags{flags}
122 mDebugGroups.emplace_back(DebugSource::Other, 0, std::string{});
123 mDebugEnabled.store(mContextFlags.test(ContextFlags::DebugBit), std::memory_order_relaxed);
126 ALCcontext::~ALCcontext()
128 TRACE("Freeing context %p\n", voidp{this});
130 size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), 0_uz,
131 [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
132 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
133 if(count > 0)
134 WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
135 mSourceList.clear();
136 mNumSources = 0;
138 #ifdef ALSOFT_EAX
139 eaxUninitialize();
140 #endif // ALSOFT_EAX
142 mDefaultSlot = nullptr;
143 count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), 0_uz,
144 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
145 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
146 if(count > 0)
147 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
148 mEffectSlotList.clear();
149 mNumEffectSlots = 0;
152 void ALCcontext::init()
154 if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
156 mDefaultSlot = std::make_unique<ALeffectslot>(this);
157 aluInitEffectPanning(mDefaultSlot->mSlot, this);
160 std::unique_ptr<EffectSlotArray> auxslots;
161 if(!mDefaultSlot)
162 auxslots = EffectSlot::CreatePtrArray(0);
163 else
165 auxslots = EffectSlot::CreatePtrArray(2);
166 (*auxslots)[0] = mDefaultSlot->mSlot;
167 (*auxslots)[1] = mDefaultSlot->mSlot;
168 mDefaultSlot->mState = SlotState::Playing;
170 mActiveAuxSlots.store(std::move(auxslots), std::memory_order_relaxed);
172 allocVoiceChanges();
174 VoiceChange *cur{mVoiceChangeTail};
175 while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
176 cur = next;
177 mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
180 mExtensions = getContextExtensions();
182 if(sBufferSubDataCompat)
184 auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"sv);
185 if(iter != mExtensions.end()) mExtensions.erase(iter);
186 /* TODO: Would be nice to sort this alphabetically. Needs case-
187 * insensitive searching.
189 mExtensions.emplace_back("AL_SOFT_buffer_sub_data"sv);
192 #ifdef ALSOFT_EAX
193 eax_initialize_extensions();
194 #endif // ALSOFT_EAX
196 if(!mExtensions.empty())
198 const size_t len{std::accumulate(mExtensions.cbegin()+1, mExtensions.cend(),
199 mExtensions.front().length(),
200 [](size_t current, std::string_view ext) noexcept
201 { return current + ext.length() + 1; })};
203 std::string extensions;
204 extensions.reserve(len);
205 extensions += mExtensions.front();
206 for(std::string_view ext : al::span{mExtensions}.subspan<1>())
208 extensions += ' ';
209 extensions += ext;
212 mExtensionsString = std::move(extensions);
215 mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
216 mParams.Matrix = alu::Matrix::Identity();
217 mParams.Velocity = alu::Vector{};
218 mParams.Gain = mListener.Gain;
219 mParams.MetersPerUnit = mListener.mMetersPerUnit;
220 mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
221 mParams.DopplerFactor = mDopplerFactor;
222 mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
223 mParams.SourceDistanceModel = mSourceDistanceModel;
224 mParams.mDistanceModel = mDistanceModel;
227 mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
228 StartEventThrd(this);
231 allocVoices(256);
232 mActiveVoiceCount.store(64, std::memory_order_relaxed);
235 void ALCcontext::deinit()
237 if(sLocalContext == this)
239 WARN("%p released while current on thread\n", voidp{this});
240 sThreadContext.set(nullptr);
241 dec_ref();
244 ALCcontext *origctx{this};
245 if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
247 while(sGlobalContextLock.load()) {
248 /* Wait to make sure another thread didn't get the context and is
249 * trying to increment its refcount.
252 dec_ref();
255 bool stopPlayback{};
256 /* First make sure this context exists in the device's list. */
257 auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
258 if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
260 using ContextArray = al::FlexArray<ContextBase*>;
261 const size_t newsize{oldarray->size() - toremove};
262 auto newarray = ContextArray::Create(newsize);
264 /* Copy the current/old context handles to the new array, excluding the
265 * given context.
267 std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
268 [this](ContextBase *ctx) { return ctx != this; });
270 /* Store the new context array in the device. Wait for any current mix
271 * to finish before deleting the old array.
273 auto prevarray = mDevice->mContexts.exchange(std::move(newarray));
274 std::ignore = mDevice->waitForMix();
276 stopPlayback = (newsize == 0);
278 else
279 stopPlayback = oldarray->empty();
281 StopEventThrd(this);
283 if(stopPlayback && mALDevice->mDeviceState == DeviceState::Playing)
285 mALDevice->Backend->stop();
286 mALDevice->mDeviceState = DeviceState::Configured;
290 void ALCcontext::applyAllUpdates()
292 /* Tell the mixer to stop applying updates, then wait for any active
293 * updating to finish, before providing updates.
295 mHoldUpdates.store(true, std::memory_order_release);
296 while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
297 /* busy-wait */
300 #ifdef ALSOFT_EAX
301 if(mEaxNeedsCommit)
302 eaxCommit();
303 #endif
305 if(std::exchange(mPropsDirty, false))
306 UpdateContextProps(this);
307 UpdateAllEffectSlotProps(this);
308 UpdateAllSourceProps(this);
310 /* Now with all updates declared, let the mixer continue applying them so
311 * they all happen at once.
313 mHoldUpdates.store(false, std::memory_order_release);
317 #ifdef ALSOFT_EAX
318 namespace {
320 template<typename F>
321 void ForEachSource(ALCcontext *context, F func)
323 for(auto &sublist : context->mSourceList)
325 uint64_t usemask{~sublist.FreeMask};
326 while(usemask)
328 const auto idx = static_cast<uint>(al::countr_zero(usemask));
329 usemask &= ~(1_u64 << idx);
331 func((*sublist.Sources)[idx]);
336 } // namespace
339 bool ALCcontext::eaxIsCapable() const noexcept
341 return eax_has_enough_aux_sends();
344 void ALCcontext::eaxUninitialize() noexcept
346 if(!mEaxIsInitialized)
347 return;
349 mEaxIsInitialized = false;
350 mEaxIsTried = false;
351 mEaxFxSlots.uninitialize();
354 ALenum ALCcontext::eax_eax_set(
355 const GUID* property_set_id,
356 ALuint property_id,
357 ALuint property_source_id,
358 ALvoid* property_value,
359 ALuint property_value_size)
361 const auto call = create_eax_call(
362 EaxCallType::set,
363 property_set_id,
364 property_id,
365 property_source_id,
366 property_value,
367 property_value_size);
369 eax_initialize();
371 switch(call.get_property_set_id())
373 case EaxCallPropertySetId::context:
374 eax_set(call);
375 break;
376 case EaxCallPropertySetId::fx_slot:
377 case EaxCallPropertySetId::fx_slot_effect:
378 eax_dispatch_fx_slot(call);
379 break;
380 case EaxCallPropertySetId::source:
381 eax_dispatch_source(call);
382 break;
383 default:
384 eax_fail_unknown_property_set_id();
386 mEaxNeedsCommit = true;
388 if(!call.is_deferred())
390 eaxCommit();
391 if(!mDeferUpdates)
392 applyAllUpdates();
395 return AL_NO_ERROR;
398 ALenum ALCcontext::eax_eax_get(
399 const GUID* property_set_id,
400 ALuint property_id,
401 ALuint property_source_id,
402 ALvoid* property_value,
403 ALuint property_value_size)
405 const auto call = create_eax_call(
406 EaxCallType::get,
407 property_set_id,
408 property_id,
409 property_source_id,
410 property_value,
411 property_value_size);
413 eax_initialize();
415 switch(call.get_property_set_id())
417 case EaxCallPropertySetId::context:
418 eax_get(call);
419 break;
420 case EaxCallPropertySetId::fx_slot:
421 case EaxCallPropertySetId::fx_slot_effect:
422 eax_dispatch_fx_slot(call);
423 break;
424 case EaxCallPropertySetId::source:
425 eax_dispatch_source(call);
426 break;
427 default:
428 eax_fail_unknown_property_set_id();
431 return AL_NO_ERROR;
434 void ALCcontext::eaxSetLastError() noexcept
436 mEaxLastError = EAXERR_INVALID_OPERATION;
439 [[noreturn]] void ALCcontext::eax_fail(const char* message)
441 throw ContextException{message};
444 [[noreturn]] void ALCcontext::eax_fail_unknown_property_set_id()
446 eax_fail("Unknown property ID.");
449 [[noreturn]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
451 eax_fail("Unknown primary FX Slot ID.");
454 [[noreturn]] void ALCcontext::eax_fail_unknown_property_id()
456 eax_fail("Unknown property ID.");
459 [[noreturn]] void ALCcontext::eax_fail_unknown_version()
461 eax_fail("Unknown version.");
464 void ALCcontext::eax_initialize_extensions()
466 if(!eax_g_is_enabled)
467 return;
469 mExtensions.emplace(mExtensions.begin(), "EAX-RAM"sv);
470 if(eaxIsCapable())
472 mExtensions.emplace(mExtensions.begin(), "EAX5.0"sv);
473 mExtensions.emplace(mExtensions.begin(), "EAX4.0"sv);
474 mExtensions.emplace(mExtensions.begin(), "EAX3.0"sv);
475 mExtensions.emplace(mExtensions.begin(), "EAX2.0"sv);
476 mExtensions.emplace(mExtensions.begin(), "EAX"sv);
480 void ALCcontext::eax_initialize()
482 if(mEaxIsInitialized)
483 return;
485 if(mEaxIsTried)
486 eax_fail("No EAX.");
488 mEaxIsTried = true;
490 if(!eax_g_is_enabled)
491 eax_fail("EAX disabled by a configuration.");
493 eax_ensure_compatibility();
494 eax_set_defaults();
495 eax_context_commit_air_absorbtion_hf();
496 eax_update_speaker_configuration();
497 eax_initialize_fx_slots();
499 mEaxIsInitialized = true;
502 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
504 return mDefaultSlot == nullptr;
507 void ALCcontext::eax_ensure_no_default_effect_slot() const
509 if(!eax_has_no_default_effect_slot())
510 eax_fail("There is a default effect slot in the context.");
513 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
515 return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS;
518 void ALCcontext::eax_ensure_enough_aux_sends() const
520 if(!eax_has_enough_aux_sends())
521 eax_fail("Not enough aux sends.");
524 void ALCcontext::eax_ensure_compatibility()
526 eax_ensure_enough_aux_sends();
529 unsigned long ALCcontext::eax_detect_speaker_configuration() const
531 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
533 switch(mDevice->FmtChans)
535 case DevFmtMono: return SPEAKERS_2;
536 case DevFmtStereo:
537 /* Pretend 7.1 if using UHJ output, since they both provide full
538 * horizontal surround.
540 if(mDevice->mUhjEncoder)
541 return SPEAKERS_7;
542 if(mDevice->Flags.test(DirectEar))
543 return HEADPHONES;
544 return SPEAKERS_2;
545 case DevFmtQuad: return SPEAKERS_4;
546 case DevFmtX51: return SPEAKERS_5;
547 case DevFmtX61: return SPEAKERS_6;
548 case DevFmtX71: return SPEAKERS_7;
549 /* 7.1.4(.4) is compatible with 7.1. This could instead be HEADPHONES to
550 * suggest with-height surround sound (like HRTF).
552 case DevFmtX714: return SPEAKERS_7;
553 case DevFmtX7144: 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);
565 return HEADPHONES;
567 #undef EAX_PREFIX
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);
597 state.d = 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);
612 state.d = 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);
620 mEax = mEax5.i;
621 mEaxVersion = 5;
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:
663 break;
664 case EAXCONTEXT_LASTERROR:
665 call.set_value<ContextException>(mEaxLastError);
666 mEaxLastError = EAX_OK;
667 break;
668 case EAXCONTEXT_SPEAKERCONFIG:
669 call.set_value<ContextException>(mEaxSpeakerConfig);
670 break;
671 case EAXCONTEXT_EAXSESSION:
672 call.set_value<ContextException>(mEaxSession);
673 break;
674 default:
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);
685 break;
686 case EAXCONTEXT_PRIMARYFXSLOTID:
687 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
688 break;
689 case EAXCONTEXT_DISTANCEFACTOR:
690 call.set_value<ContextException>(props.flDistanceFactor);
691 break;
692 case EAXCONTEXT_AIRABSORPTIONHF:
693 call.set_value<ContextException>(props.flAirAbsorptionHF);
694 break;
695 case EAXCONTEXT_HFREFERENCE:
696 call.set_value<ContextException>(props.flHFReference);
697 break;
698 default:
699 eax_get_misc(call);
700 break;
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);
710 break;
711 case EAXCONTEXT_PRIMARYFXSLOTID:
712 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
713 break;
714 case EAXCONTEXT_DISTANCEFACTOR:
715 call.set_value<ContextException>(props.flDistanceFactor);
716 break;
717 case EAXCONTEXT_AIRABSORPTIONHF:
718 call.set_value<ContextException>(props.flAirAbsorptionHF);
719 break;
720 case EAXCONTEXT_HFREFERENCE:
721 call.set_value<ContextException>(props.flHFReference);
722 break;
723 case EAXCONTEXT_MACROFXFACTOR:
724 call.set_value<ContextException>(props.flMacroFXFactor);
725 break;
726 default:
727 eax_get_misc(call);
728 break;
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)
750 return;
752 mListener.mMetersPerUnit = mEax.flDistanceFactor;
753 mPropsDirty = true;
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)
761 return;
763 mAirAbsorptionGainHF = new_value;
764 mPropsDirty = true;
767 void ALCcontext::eax_context_commit_hf_reference()
769 // TODO
772 void ALCcontext::eax_context_commit_macro_fx_factor()
774 // TODO
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:
796 break;
797 case EAXCONTEXT_SPEAKERCONFIG:
798 eax_set<Eax5SpeakerConfigValidator>(call, mEaxSpeakerConfig);
799 break;
800 case EAXCONTEXT_EAXSESSION:
801 eax_set<Eax5SessionAllValidator>(call, mEaxSession);
802 break;
803 default:
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;
814 dst_d = src;
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);
835 break;
836 case EAXCONTEXT_PRIMARYFXSLOTID:
837 eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
838 call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
839 break;
840 case EAXCONTEXT_DISTANCEFACTOR:
841 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
842 call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
843 break;
844 case EAXCONTEXT_AIRABSORPTIONHF:
845 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
846 call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
847 break;
848 case EAXCONTEXT_HFREFERENCE:
849 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
850 call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
851 break;
852 default:
853 eax_set_misc(call);
854 break;
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;
864 dst_d = src;
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);
888 break;
889 case EAXCONTEXT_PRIMARYFXSLOTID:
890 eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
891 call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
892 break;
893 case EAXCONTEXT_DISTANCEFACTOR:
894 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
895 call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
896 break;
897 case EAXCONTEXT_AIRABSORPTIONHF:
898 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
899 call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
900 break;
901 case EAXCONTEXT_HFREFERENCE:
902 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
903 call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
904 break;
905 case EAXCONTEXT_MACROFXFACTOR:
906 eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
907 call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
908 break;
909 default:
910 eax_set_misc(call);
911 break;
915 void ALCcontext::eax_set(const EaxCall& call)
917 const auto version = call.get_version();
918 switch(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{})
932 return;
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{})
949 return;
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{};
969 switch(mEaxVersion)
971 case 1:
972 case 2:
973 case 3:
974 eax5_context_commit(mEax123, dst_df);
975 break;
976 case 4:
977 eax4_context_commit(mEax4, dst_df);
978 break;
979 case 5:
980 eax5_context_commit(mEax5, dst_df);
981 break;
984 if(dst_df == EaxDirtyFlags{})
985 return;
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();
1010 eaxCommitFxSlots();
1011 eax_update_sources();
1015 FORCE_ALIGN auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
1016 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
1018 auto context = GetContextRef();
1019 if(!context) UNLIKELY return AL_INVALID_OPERATION;
1020 return EAXSetDirect(context.get(), property_set_id, property_id, source_id, value, value_size);
1023 FORCE_ALIGN auto AL_APIENTRY EAXSetDirect(ALCcontext *context, const GUID *property_set_id,
1024 ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
1027 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1028 return context->eax_eax_set(property_set_id, property_id, source_id, value, value_size);
1030 catch(...)
1032 context->eaxSetLastError();
1033 eax_log_exception(std::data(__func__));
1034 return AL_INVALID_OPERATION;
1038 FORCE_ALIGN auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id,
1039 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
1041 auto context = GetContextRef();
1042 if(!context) UNLIKELY return AL_INVALID_OPERATION;
1043 return EAXGetDirect(context.get(), property_set_id, property_id, source_id, value, value_size);
1046 FORCE_ALIGN auto AL_APIENTRY EAXGetDirect(ALCcontext *context, const GUID *property_set_id,
1047 ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
1050 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1051 return context->eax_eax_get(property_set_id, property_id, source_id, value, value_size);
1053 catch(...)
1055 context->eaxSetLastError();
1056 eax_log_exception(std::data(__func__));
1057 return AL_INVALID_OPERATION;
1059 #endif // ALSOFT_EAX