Update an outdated comment
[openal-soft.git] / alc / context.cpp
blob7c6a2d67897b12629c0f96802753f50c372fbbd0
2 #include "config.h"
4 #include "context.h"
6 #include <algorithm>
7 #include <array>
8 #include <cstddef>
9 #include <functional>
10 #include <iterator>
11 #include <numeric>
12 #include <optional>
13 #include <string_view>
14 #include <tuple>
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 "alnumeric.h"
29 #include "alspan.h"
30 #include "atomic.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"
37 #include "device.h"
38 #include "flexarray.h"
39 #include "ringbuffer.h"
40 #include "vecmat.h"
42 #ifdef ALSOFT_EAX
43 #include "al/eax/call.h"
44 #include "al/eax/globals.h"
45 #endif // ALSOFT_EAX
47 namespace {
49 using namespace std::string_view_literals;
50 using voidp = void*;
52 /* Default context extensions */
53 std::vector<std::string_view> getContextExtensions() noexcept
55 return std::vector<std::string_view>{
56 "AL_EXT_ALAW"sv,
57 "AL_EXT_BFORMAT"sv,
58 "AL_EXT_debug"sv,
59 "AL_EXT_direct_context"sv,
60 "AL_EXT_DOUBLE"sv,
61 "AL_EXT_EXPONENT_DISTANCE"sv,
62 "AL_EXT_FLOAT32"sv,
63 "AL_EXT_IMA4"sv,
64 "AL_EXT_LINEAR_DISTANCE"sv,
65 "AL_EXT_MCFORMATS"sv,
66 "AL_EXT_MULAW"sv,
67 "AL_EXT_MULAW_BFORMAT"sv,
68 "AL_EXT_MULAW_MCFORMATS"sv,
69 "AL_EXT_OFFSET"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,
85 "AL_SOFT_events"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,
90 "AL_SOFT_MSADPCM"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,
97 "AL_SOFT_UHJ"sv,
98 "AL_SOFT_UHJ_ex"sv,
102 } // namespace
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<ALCdevice> 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)); })};
140 if(count > 0)
141 WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
142 mSourceList.clear();
143 mNumSources = 0;
145 #ifdef ALSOFT_EAX
146 eaxUninitialize();
147 #endif // ALSOFT_EAX
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)); });
153 if(count > 0)
154 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
155 mEffectSlotList.clear();
156 mNumEffectSlots = 0;
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;
168 if(!mDefaultSlot)
169 auxslots = EffectSlot::CreatePtrArray(0);
170 else
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);
179 allocVoiceChanges();
181 VoiceChange *cur{mVoiceChangeTail};
182 while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
183 cur = next;
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);
199 #ifdef ALSOFT_EAX
200 eax_initialize_extensions();
201 #endif // ALSOFT_EAX
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>())
215 extensions += ' ';
216 extensions += ext;
219 mExtensionsString = std::move(extensions);
222 #ifdef ALSOFT_EAX
223 eax_set_defaults();
224 #endif
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
231 #ifdef ALSOFT_EAX
232 * eaxGetDistanceFactor()
233 #endif
235 mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
236 mParams.DopplerFactor = mDopplerFactor;
237 mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity
238 #ifdef ALSOFT_EAX
239 / eaxGetDistanceFactor()
240 #endif
242 mParams.SourceDistanceModel = mSourceDistanceModel;
243 mParams.mDistanceModel = mDistanceModel;
246 mAsyncEvents = RingBuffer::Create(1024, sizeof(AsyncEvent), false);
247 StartEventThrd(this);
250 allocVoices(256);
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 dec_ref();
263 ALCcontext *origctx{this};
264 if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
266 while(sGlobalContextLock.load()) {
267 /* Wait to make sure another thread didn't get the context and is
268 * trying to increment its refcount.
271 dec_ref();
274 bool stopPlayback{};
275 /* First make sure this context exists in the device's list. */
276 auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
277 if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
279 using ContextArray = al::FlexArray<ContextBase*>;
280 const size_t newsize{oldarray->size() - toremove};
281 auto newarray = ContextArray::Create(newsize);
283 /* Copy the current/old context handles to the new array, excluding the
284 * given context.
286 std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
287 [this](ContextBase *ctx) { return ctx != this; });
289 /* Store the new context array in the device. Wait for any current mix
290 * to finish before deleting the old array.
292 auto prevarray = mDevice->mContexts.exchange(std::move(newarray));
293 std::ignore = mDevice->waitForMix();
295 stopPlayback = (newsize == 0);
297 else
298 stopPlayback = oldarray->empty();
300 StopEventThrd(this);
302 if(stopPlayback && mALDevice->mDeviceState == DeviceState::Playing)
304 mALDevice->Backend->stop();
305 mALDevice->mDeviceState = DeviceState::Configured;
309 void ALCcontext::applyAllUpdates()
311 /* Tell the mixer to stop applying updates, then wait for any active
312 * updating to finish, before providing updates.
314 mHoldUpdates.store(true, std::memory_order_release);
315 while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
316 /* busy-wait */
319 #ifdef ALSOFT_EAX
320 if(mEaxNeedsCommit)
321 eaxCommit();
322 #endif
324 if(std::exchange(mPropsDirty, false))
325 UpdateContextProps(this);
326 UpdateAllEffectSlotProps(this);
327 UpdateAllSourceProps(this);
329 /* Now with all updates declared, let the mixer continue applying them so
330 * they all happen at once.
332 mHoldUpdates.store(false, std::memory_order_release);
336 #ifdef ALSOFT_EAX
337 namespace {
339 template<typename F>
340 void ForEachSource(ALCcontext *context, F func)
342 for(auto &sublist : context->mSourceList)
344 uint64_t usemask{~sublist.FreeMask};
345 while(usemask)
347 const auto idx = static_cast<uint>(al::countr_zero(usemask));
348 usemask &= ~(1_u64 << idx);
350 func((*sublist.Sources)[idx]);
355 } // namespace
358 bool ALCcontext::eaxIsCapable() const noexcept
360 return eax_has_enough_aux_sends();
363 void ALCcontext::eaxUninitialize() noexcept
365 if(!mEaxIsInitialized)
366 return;
368 mEaxIsInitialized = false;
369 mEaxIsTried = false;
370 mEaxFxSlots.uninitialize();
373 ALenum ALCcontext::eax_eax_set(
374 const GUID* property_set_id,
375 ALuint property_id,
376 ALuint property_source_id,
377 ALvoid* property_value,
378 ALuint property_value_size)
380 const auto call = create_eax_call(
381 EaxCallType::set,
382 property_set_id,
383 property_id,
384 property_source_id,
385 property_value,
386 property_value_size);
388 eax_initialize();
390 switch(call.get_property_set_id())
392 case EaxCallPropertySetId::context:
393 eax_set(call);
394 break;
395 case EaxCallPropertySetId::fx_slot:
396 case EaxCallPropertySetId::fx_slot_effect:
397 eax_dispatch_fx_slot(call);
398 break;
399 case EaxCallPropertySetId::source:
400 eax_dispatch_source(call);
401 break;
402 default:
403 eax_fail_unknown_property_set_id();
405 mEaxNeedsCommit = true;
407 if(!call.is_deferred())
409 eaxCommit();
410 if(!mDeferUpdates)
411 applyAllUpdates();
414 return AL_NO_ERROR;
417 ALenum ALCcontext::eax_eax_get(
418 const GUID* property_set_id,
419 ALuint property_id,
420 ALuint property_source_id,
421 ALvoid* property_value,
422 ALuint property_value_size)
424 const auto call = create_eax_call(
425 EaxCallType::get,
426 property_set_id,
427 property_id,
428 property_source_id,
429 property_value,
430 property_value_size);
432 eax_initialize();
434 switch(call.get_property_set_id())
436 case EaxCallPropertySetId::context:
437 eax_get(call);
438 break;
439 case EaxCallPropertySetId::fx_slot:
440 case EaxCallPropertySetId::fx_slot_effect:
441 eax_dispatch_fx_slot(call);
442 break;
443 case EaxCallPropertySetId::source:
444 eax_dispatch_source(call);
445 break;
446 default:
447 eax_fail_unknown_property_set_id();
450 return AL_NO_ERROR;
453 void ALCcontext::eaxSetLastError() noexcept
455 mEaxLastError = EAXERR_INVALID_OPERATION;
458 [[noreturn]] void ALCcontext::eax_fail(const char* message)
460 throw ContextException{message};
463 [[noreturn]] void ALCcontext::eax_fail_unknown_property_set_id()
465 eax_fail("Unknown property ID.");
468 [[noreturn]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
470 eax_fail("Unknown primary FX Slot ID.");
473 [[noreturn]] void ALCcontext::eax_fail_unknown_property_id()
475 eax_fail("Unknown property ID.");
478 [[noreturn]] void ALCcontext::eax_fail_unknown_version()
480 eax_fail("Unknown version.");
483 void ALCcontext::eax_initialize_extensions()
485 if(!eax_g_is_enabled)
486 return;
488 mExtensions.emplace(mExtensions.begin(), "EAX-RAM"sv);
489 if(eaxIsCapable())
491 mExtensions.emplace(mExtensions.begin(), "EAX5.0"sv);
492 mExtensions.emplace(mExtensions.begin(), "EAX4.0"sv);
493 mExtensions.emplace(mExtensions.begin(), "EAX3.0"sv);
494 mExtensions.emplace(mExtensions.begin(), "EAX2.0"sv);
495 mExtensions.emplace(mExtensions.begin(), "EAX"sv);
499 void ALCcontext::eax_initialize()
501 if(mEaxIsInitialized)
502 return;
504 if(mEaxIsTried)
505 eax_fail("No EAX.");
507 mEaxIsTried = true;
509 if(!eax_g_is_enabled)
510 eax_fail("EAX disabled by a configuration.");
512 eax_ensure_compatibility();
513 eax_set_defaults();
514 eax_context_commit_air_absorbtion_hf();
515 eax_update_speaker_configuration();
516 eax_initialize_fx_slots();
518 mEaxIsInitialized = true;
521 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
523 return mDefaultSlot == nullptr;
526 void ALCcontext::eax_ensure_no_default_effect_slot() const
528 if(!eax_has_no_default_effect_slot())
529 eax_fail("There is a default effect slot in the context.");
532 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
534 return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS;
537 void ALCcontext::eax_ensure_enough_aux_sends() const
539 if(!eax_has_enough_aux_sends())
540 eax_fail("Not enough aux sends.");
543 void ALCcontext::eax_ensure_compatibility()
545 eax_ensure_enough_aux_sends();
548 unsigned long ALCcontext::eax_detect_speaker_configuration() const
550 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
552 switch(mDevice->FmtChans)
554 case DevFmtMono: return SPEAKERS_2;
555 case DevFmtStereo:
556 /* Pretend 7.1 if using UHJ output, since they both provide full
557 * horizontal surround.
559 if(mDevice->mUhjEncoder)
560 return SPEAKERS_7;
561 if(mDevice->Flags.test(DirectEar))
562 return HEADPHONES;
563 return SPEAKERS_2;
564 case DevFmtQuad: return SPEAKERS_4;
565 case DevFmtX51: return SPEAKERS_5;
566 case DevFmtX61: return SPEAKERS_6;
567 case DevFmtX71: return SPEAKERS_7;
568 /* 7.1.4(.4) is compatible with 7.1. This could instead be HEADPHONES to
569 * suggest with-height surround sound (like HRTF).
571 case DevFmtX714: return SPEAKERS_7;
572 case DevFmtX7144: return SPEAKERS_7;
573 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
574 * suggest full-sphere surround sound (like HRTF).
576 case DevFmtX3D71: return SPEAKERS_5;
577 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
578 * provide full-sphere surround sound. Depends if apps are more likely to
579 * consider headphones or 7.1 for surround sound support.
581 case DevFmtAmbi3D: return SPEAKERS_7;
583 ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
584 return HEADPHONES;
586 #undef EAX_PREFIX
589 void ALCcontext::eax_update_speaker_configuration()
591 mEaxSpeakerConfig = eax_detect_speaker_configuration();
594 void ALCcontext::eax_set_last_error_defaults() noexcept
596 mEaxLastError = EAXCONTEXT_DEFAULTLASTERROR;
599 void ALCcontext::eax_session_set_defaults() noexcept
601 mEaxSession.ulEAXVersion = EAXCONTEXT_DEFAULTEAXSESSION;
602 mEaxSession.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
605 void ALCcontext::eax4_context_set_defaults(Eax4Props& props) noexcept
607 props.guidPrimaryFXSlotID = EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
608 props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
609 props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
610 props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
613 void ALCcontext::eax4_context_set_defaults(Eax4State& state) noexcept
615 eax4_context_set_defaults(state.i);
616 state.d = state.i;
619 void ALCcontext::eax5_context_set_defaults(Eax5Props& props) noexcept
621 props.guidPrimaryFXSlotID = EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID;
622 props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
623 props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
624 props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
625 props.flMacroFXFactor = EAXCONTEXT_DEFAULTMACROFXFACTOR;
628 void ALCcontext::eax5_context_set_defaults(Eax5State& state) noexcept
630 eax5_context_set_defaults(state.i);
631 state.d = state.i;
634 void ALCcontext::eax_context_set_defaults()
636 eax5_context_set_defaults(mEax123);
637 eax4_context_set_defaults(mEax4);
638 eax5_context_set_defaults(mEax5);
639 mEax = mEax5.i;
640 mEaxVersion = 5;
641 mEaxDf = EaxDirtyFlags{};
644 void ALCcontext::eax_set_defaults()
646 eax_set_last_error_defaults();
647 eax_session_set_defaults();
648 eax_context_set_defaults();
651 void ALCcontext::eax_dispatch_fx_slot(const EaxCall& call)
653 const auto fx_slot_index = call.get_fx_slot_index();
654 if(!fx_slot_index.has_value())
655 eax_fail("Invalid fx slot index.");
657 auto& fx_slot = eaxGetFxSlot(*fx_slot_index);
658 if(fx_slot.eax_dispatch(call))
660 std::lock_guard<std::mutex> source_lock{mSourceLock};
661 ForEachSource(this, std::mem_fn(&ALsource::eaxMarkAsChanged));
665 void ALCcontext::eax_dispatch_source(const EaxCall& call)
667 const auto source_id = call.get_property_al_name();
668 std::lock_guard<std::mutex> source_lock{mSourceLock};
669 const auto source = ALsource::EaxLookupSource(*this, source_id);
671 if (source == nullptr)
672 eax_fail("Source not found.");
674 source->eaxDispatch(call);
677 void ALCcontext::eax_get_misc(const EaxCall& call)
679 switch(call.get_property_id())
681 case EAXCONTEXT_NONE:
682 break;
683 case EAXCONTEXT_LASTERROR:
684 call.set_value<ContextException>(mEaxLastError);
685 mEaxLastError = EAX_OK;
686 break;
687 case EAXCONTEXT_SPEAKERCONFIG:
688 call.set_value<ContextException>(mEaxSpeakerConfig);
689 break;
690 case EAXCONTEXT_EAXSESSION:
691 call.set_value<ContextException>(mEaxSession);
692 break;
693 default:
694 eax_fail_unknown_property_id();
698 void ALCcontext::eax4_get(const EaxCall& call, const Eax4Props& props)
700 switch(call.get_property_id())
702 case EAXCONTEXT_ALLPARAMETERS:
703 call.set_value<ContextException>(props);
704 break;
705 case EAXCONTEXT_PRIMARYFXSLOTID:
706 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
707 break;
708 case EAXCONTEXT_DISTANCEFACTOR:
709 call.set_value<ContextException>(props.flDistanceFactor);
710 break;
711 case EAXCONTEXT_AIRABSORPTIONHF:
712 call.set_value<ContextException>(props.flAirAbsorptionHF);
713 break;
714 case EAXCONTEXT_HFREFERENCE:
715 call.set_value<ContextException>(props.flHFReference);
716 break;
717 default:
718 eax_get_misc(call);
719 break;
723 void ALCcontext::eax5_get(const EaxCall& call, const Eax5Props& props)
725 switch(call.get_property_id())
727 case EAXCONTEXT_ALLPARAMETERS:
728 call.set_value<ContextException>(props);
729 break;
730 case EAXCONTEXT_PRIMARYFXSLOTID:
731 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
732 break;
733 case EAXCONTEXT_DISTANCEFACTOR:
734 call.set_value<ContextException>(props.flDistanceFactor);
735 break;
736 case EAXCONTEXT_AIRABSORPTIONHF:
737 call.set_value<ContextException>(props.flAirAbsorptionHF);
738 break;
739 case EAXCONTEXT_HFREFERENCE:
740 call.set_value<ContextException>(props.flHFReference);
741 break;
742 case EAXCONTEXT_MACROFXFACTOR:
743 call.set_value<ContextException>(props.flMacroFXFactor);
744 break;
745 default:
746 eax_get_misc(call);
747 break;
751 void ALCcontext::eax_get(const EaxCall& call)
753 switch(call.get_version())
755 case 4: eax4_get(call, mEax4.i); break;
756 case 5: eax5_get(call, mEax5.i); break;
757 default: eax_fail_unknown_version();
761 void ALCcontext::eax_context_commit_primary_fx_slot_id()
763 mEaxPrimaryFxSlotIndex = mEax.guidPrimaryFXSlotID;
766 void ALCcontext::eax_context_commit_distance_factor()
768 /* mEax.flDistanceFactor was changed, so the context props are dirty. */
769 mPropsDirty = true;
772 void ALCcontext::eax_context_commit_air_absorbtion_hf()
774 const auto new_value = level_mb_to_gain(mEax.flAirAbsorptionHF);
776 if(mAirAbsorptionGainHF == new_value)
777 return;
779 mAirAbsorptionGainHF = new_value;
780 mPropsDirty = true;
783 void ALCcontext::eax_context_commit_hf_reference()
785 // TODO
788 void ALCcontext::eax_context_commit_macro_fx_factor()
790 // TODO
793 void ALCcontext::eax_initialize_fx_slots()
795 mEaxFxSlots.initialize(*this);
796 mEaxPrimaryFxSlotIndex = mEax.guidPrimaryFXSlotID;
799 void ALCcontext::eax_update_sources()
801 std::unique_lock<std::mutex> source_lock{mSourceLock};
802 auto update_source = [](ALsource &source)
803 { source.eaxCommit(); };
804 ForEachSource(this, update_source);
807 void ALCcontext::eax_set_misc(const EaxCall& call)
809 switch(call.get_property_id())
811 case EAXCONTEXT_NONE:
812 break;
813 case EAXCONTEXT_SPEAKERCONFIG:
814 eax_set<Eax5SpeakerConfigValidator>(call, mEaxSpeakerConfig);
815 break;
816 case EAXCONTEXT_EAXSESSION:
817 eax_set<Eax5SessionAllValidator>(call, mEaxSession);
818 break;
819 default:
820 eax_fail_unknown_property_id();
824 void ALCcontext::eax4_defer_all(const EaxCall& call, Eax4State& state)
826 const auto& src = call.get_value<ContextException, const EAX40CONTEXTPROPERTIES>();
827 Eax4AllValidator{}(src);
828 const auto& dst_i = state.i;
829 auto& dst_d = state.d;
830 dst_d = src;
832 if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
833 mEaxDf |= eax_primary_fx_slot_id_dirty_bit;
835 if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
836 mEaxDf |= eax_distance_factor_dirty_bit;
838 if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
839 mEaxDf |= eax_air_absorption_hf_dirty_bit;
841 if(dst_i.flHFReference != dst_d.flHFReference)
842 mEaxDf |= eax_hf_reference_dirty_bit;
845 void ALCcontext::eax4_defer(const EaxCall& call, Eax4State& state)
847 switch(call.get_property_id())
849 case EAXCONTEXT_ALLPARAMETERS:
850 eax4_defer_all(call, state);
851 break;
852 case EAXCONTEXT_PRIMARYFXSLOTID:
853 eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
854 call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
855 break;
856 case EAXCONTEXT_DISTANCEFACTOR:
857 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
858 call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
859 break;
860 case EAXCONTEXT_AIRABSORPTIONHF:
861 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
862 call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
863 break;
864 case EAXCONTEXT_HFREFERENCE:
865 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
866 call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
867 break;
868 default:
869 eax_set_misc(call);
870 break;
874 void ALCcontext::eax5_defer_all(const EaxCall& call, Eax5State& state)
876 const auto& src = call.get_value<ContextException, const EAX50CONTEXTPROPERTIES>();
877 Eax4AllValidator{}(src);
878 const auto& dst_i = state.i;
879 auto& dst_d = state.d;
880 dst_d = src;
882 if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
883 mEaxDf |= eax_primary_fx_slot_id_dirty_bit;
885 if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
886 mEaxDf |= eax_distance_factor_dirty_bit;
888 if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
889 mEaxDf |= eax_air_absorption_hf_dirty_bit;
891 if(dst_i.flHFReference != dst_d.flHFReference)
892 mEaxDf |= eax_hf_reference_dirty_bit;
894 if(dst_i.flMacroFXFactor != dst_d.flMacroFXFactor)
895 mEaxDf |= eax_macro_fx_factor_dirty_bit;
898 void ALCcontext::eax5_defer(const EaxCall& call, Eax5State& state)
900 switch(call.get_property_id())
902 case EAXCONTEXT_ALLPARAMETERS:
903 eax5_defer_all(call, state);
904 break;
905 case EAXCONTEXT_PRIMARYFXSLOTID:
906 eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
907 call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
908 break;
909 case EAXCONTEXT_DISTANCEFACTOR:
910 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
911 call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
912 break;
913 case EAXCONTEXT_AIRABSORPTIONHF:
914 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
915 call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
916 break;
917 case EAXCONTEXT_HFREFERENCE:
918 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
919 call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
920 break;
921 case EAXCONTEXT_MACROFXFACTOR:
922 eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
923 call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
924 break;
925 default:
926 eax_set_misc(call);
927 break;
931 void ALCcontext::eax_set(const EaxCall& call)
933 const auto version = call.get_version();
934 switch(version)
936 case 4: eax4_defer(call, mEax4); break;
937 case 5: eax5_defer(call, mEax5); break;
938 default: eax_fail_unknown_version();
940 if(version != mEaxVersion)
941 mEaxDf = ~EaxDirtyFlags();
942 mEaxVersion = version;
945 void ALCcontext::eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df)
947 if(mEaxDf == EaxDirtyFlags{})
948 return;
950 eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
951 state, dst_df, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
952 eax_context_commit_property<eax_distance_factor_dirty_bit>(
953 state, dst_df, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
954 eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
955 state, dst_df, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
956 eax_context_commit_property<eax_hf_reference_dirty_bit>(
957 state, dst_df, &EAX40CONTEXTPROPERTIES::flHFReference);
959 mEaxDf = EaxDirtyFlags{};
962 void ALCcontext::eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df)
964 if(mEaxDf == EaxDirtyFlags{})
965 return;
967 eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
968 state, dst_df, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
969 eax_context_commit_property<eax_distance_factor_dirty_bit>(
970 state, dst_df, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
971 eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
972 state, dst_df, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
973 eax_context_commit_property<eax_hf_reference_dirty_bit>(
974 state, dst_df, &EAX50CONTEXTPROPERTIES::flHFReference);
975 eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(
976 state, dst_df, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
978 mEaxDf = EaxDirtyFlags{};
981 void ALCcontext::eax_context_commit()
983 auto dst_df = EaxDirtyFlags{};
985 switch(mEaxVersion)
987 case 1:
988 case 2:
989 case 3:
990 eax5_context_commit(mEax123, dst_df);
991 break;
992 case 4:
993 eax4_context_commit(mEax4, dst_df);
994 break;
995 case 5:
996 eax5_context_commit(mEax5, dst_df);
997 break;
1000 if(dst_df == EaxDirtyFlags{})
1001 return;
1003 if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
1004 eax_context_commit_primary_fx_slot_id();
1006 if((dst_df & eax_distance_factor_dirty_bit) != EaxDirtyFlags{})
1007 eax_context_commit_distance_factor();
1009 if((dst_df & eax_air_absorption_hf_dirty_bit) != EaxDirtyFlags{})
1010 eax_context_commit_air_absorbtion_hf();
1012 if((dst_df & eax_hf_reference_dirty_bit) != EaxDirtyFlags{})
1013 eax_context_commit_hf_reference();
1015 if((dst_df & eax_macro_fx_factor_dirty_bit) != EaxDirtyFlags{})
1016 eax_context_commit_macro_fx_factor();
1018 if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
1019 eax_update_sources();
1022 void ALCcontext::eaxCommit()
1024 mEaxNeedsCommit = false;
1025 eax_context_commit();
1026 eaxCommitFxSlots();
1027 eax_update_sources();
1031 FORCE_ALIGN auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
1032 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
1034 auto context = GetContextRef();
1035 if(!context) UNLIKELY return AL_INVALID_OPERATION;
1036 return EAXSetDirect(context.get(), property_set_id, property_id, source_id, value, value_size);
1039 FORCE_ALIGN auto AL_APIENTRY EAXSetDirect(ALCcontext *context, const GUID *property_set_id,
1040 ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
1043 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1044 return context->eax_eax_set(property_set_id, property_id, source_id, value, value_size);
1046 catch(...)
1048 context->eaxSetLastError();
1049 eax_log_exception(std::data(__func__));
1050 return AL_INVALID_OPERATION;
1054 FORCE_ALIGN auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id,
1055 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
1057 auto context = GetContextRef();
1058 if(!context) UNLIKELY return AL_INVALID_OPERATION;
1059 return EAXGetDirect(context.get(), property_set_id, property_id, source_id, value, value_size);
1062 FORCE_ALIGN auto AL_APIENTRY EAXGetDirect(ALCcontext *context, const GUID *property_set_id,
1063 ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
1066 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1067 return context->eax_eax_get(property_set_id, property_id, source_id, value, value_size);
1069 catch(...)
1071 context->eaxSetLastError();
1072 eax_log_exception(std::data(__func__));
1073 return AL_INVALID_OPERATION;
1075 #endif // ALSOFT_EAX