Disable notification and low-severity debug messages by default
[openal-soft.git] / alc / context.cpp
blob9c2183f5f6fc8acdbcd9ea81c0c575464b137bb6
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 /* Notification and low-severity debug messages are disabled by default. */
129 alDebugMessageControlDirectEXT(this, AL_DONT_CARE_EXT, AL_DONT_CARE_EXT,
130 AL_DEBUG_SEVERITY_NOTIFICATION_EXT, 0, nullptr, AL_FALSE);
131 alDebugMessageControlDirectEXT(this, AL_DONT_CARE_EXT, AL_DONT_CARE_EXT,
132 AL_DEBUG_SEVERITY_LOW_EXT, 0, nullptr, AL_FALSE);
135 ALCcontext::~ALCcontext()
137 TRACE("Freeing context %p\n", voidp{this});
139 size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), 0_uz,
140 [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
141 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
142 if(count > 0)
143 WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
144 mSourceList.clear();
145 mNumSources = 0;
147 #ifdef ALSOFT_EAX
148 eaxUninitialize();
149 #endif // ALSOFT_EAX
151 mDefaultSlot = nullptr;
152 count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), 0_uz,
153 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
154 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
155 if(count > 0)
156 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
157 mEffectSlotList.clear();
158 mNumEffectSlots = 0;
161 void ALCcontext::init()
163 if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
165 mDefaultSlot = std::make_unique<ALeffectslot>(this);
166 aluInitEffectPanning(mDefaultSlot->mSlot, this);
169 std::unique_ptr<EffectSlotArray> auxslots;
170 if(!mDefaultSlot)
171 auxslots = EffectSlot::CreatePtrArray(0);
172 else
174 auxslots = EffectSlot::CreatePtrArray(2);
175 (*auxslots)[0] = mDefaultSlot->mSlot;
176 (*auxslots)[1] = mDefaultSlot->mSlot;
177 mDefaultSlot->mState = SlotState::Playing;
179 mActiveAuxSlots.store(std::move(auxslots), std::memory_order_relaxed);
181 allocVoiceChanges();
183 VoiceChange *cur{mVoiceChangeTail};
184 while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
185 cur = next;
186 mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
189 mExtensions = getContextExtensions();
191 if(sBufferSubDataCompat)
193 auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"sv);
194 if(iter != mExtensions.end()) mExtensions.erase(iter);
195 /* TODO: Would be nice to sort this alphabetically. Needs case-
196 * insensitive searching.
198 mExtensions.emplace_back("AL_SOFT_buffer_sub_data"sv);
201 #ifdef ALSOFT_EAX
202 eax_initialize_extensions();
203 #endif // ALSOFT_EAX
205 if(!mExtensions.empty())
207 const size_t len{std::accumulate(mExtensions.cbegin()+1, mExtensions.cend(),
208 mExtensions.front().length(),
209 [](size_t current, std::string_view ext) noexcept
210 { return current + ext.length() + 1; })};
212 std::string extensions;
213 extensions.reserve(len);
214 extensions += mExtensions.front();
215 for(std::string_view ext : al::span{mExtensions}.subspan<1>())
217 extensions += ' ';
218 extensions += ext;
221 mExtensionsString = std::move(extensions);
224 #ifdef ALSOFT_EAX
225 eax_set_defaults();
226 #endif
228 mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
229 mParams.Matrix = alu::Matrix::Identity();
230 mParams.Velocity = alu::Vector{};
231 mParams.Gain = mListener.Gain;
232 mParams.MetersPerUnit = mListener.mMetersPerUnit
233 #ifdef ALSOFT_EAX
234 * eaxGetDistanceFactor()
235 #endif
237 mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
238 mParams.DopplerFactor = mDopplerFactor;
239 mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity
240 #ifdef ALSOFT_EAX
241 / eaxGetDistanceFactor()
242 #endif
244 mParams.SourceDistanceModel = mSourceDistanceModel;
245 mParams.mDistanceModel = mDistanceModel;
248 mAsyncEvents = RingBuffer::Create(1024, sizeof(AsyncEvent), false);
249 StartEventThrd(this);
252 allocVoices(256);
253 mActiveVoiceCount.store(64, std::memory_order_relaxed);
256 void ALCcontext::deinit()
258 if(sLocalContext == this)
260 WARN("%p released while current on thread\n", voidp{this});
261 sThreadContext.set(nullptr);
262 dec_ref();
265 ALCcontext *origctx{this};
266 if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
268 while(sGlobalContextLock.load()) {
269 /* Wait to make sure another thread didn't get the context and is
270 * trying to increment its refcount.
273 dec_ref();
276 bool stopPlayback{};
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
286 * given context.
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);
299 else
300 stopPlayback = oldarray->empty();
302 StopEventThrd(this);
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) {
318 /* busy-wait */
321 #ifdef ALSOFT_EAX
322 if(mEaxNeedsCommit)
323 eaxCommit();
324 #endif
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);
338 #ifdef ALSOFT_EAX
339 namespace {
341 template<typename F>
342 void ForEachSource(ALCcontext *context, F func)
344 for(auto &sublist : context->mSourceList)
346 uint64_t usemask{~sublist.FreeMask};
347 while(usemask)
349 const auto idx = static_cast<uint>(al::countr_zero(usemask));
350 usemask &= ~(1_u64 << idx);
352 func((*sublist.Sources)[idx]);
357 } // namespace
360 bool ALCcontext::eaxIsCapable() const noexcept
362 return eax_has_enough_aux_sends();
365 void ALCcontext::eaxUninitialize() noexcept
367 if(!mEaxIsInitialized)
368 return;
370 mEaxIsInitialized = false;
371 mEaxIsTried = false;
372 mEaxFxSlots.uninitialize();
375 ALenum ALCcontext::eax_eax_set(
376 const GUID* property_set_id,
377 ALuint property_id,
378 ALuint property_source_id,
379 ALvoid* property_value,
380 ALuint property_value_size)
382 const auto call = create_eax_call(
383 EaxCallType::set,
384 property_set_id,
385 property_id,
386 property_source_id,
387 property_value,
388 property_value_size);
390 eax_initialize();
392 switch(call.get_property_set_id())
394 case EaxCallPropertySetId::context:
395 eax_set(call);
396 break;
397 case EaxCallPropertySetId::fx_slot:
398 case EaxCallPropertySetId::fx_slot_effect:
399 eax_dispatch_fx_slot(call);
400 break;
401 case EaxCallPropertySetId::source:
402 eax_dispatch_source(call);
403 break;
404 default:
405 eax_fail_unknown_property_set_id();
407 mEaxNeedsCommit = true;
409 if(!call.is_deferred())
411 eaxCommit();
412 if(!mDeferUpdates)
413 applyAllUpdates();
416 return AL_NO_ERROR;
419 ALenum ALCcontext::eax_eax_get(
420 const GUID* property_set_id,
421 ALuint property_id,
422 ALuint property_source_id,
423 ALvoid* property_value,
424 ALuint property_value_size)
426 const auto call = create_eax_call(
427 EaxCallType::get,
428 property_set_id,
429 property_id,
430 property_source_id,
431 property_value,
432 property_value_size);
434 eax_initialize();
436 switch(call.get_property_set_id())
438 case EaxCallPropertySetId::context:
439 eax_get(call);
440 break;
441 case EaxCallPropertySetId::fx_slot:
442 case EaxCallPropertySetId::fx_slot_effect:
443 eax_dispatch_fx_slot(call);
444 break;
445 case EaxCallPropertySetId::source:
446 eax_dispatch_source(call);
447 break;
448 default:
449 eax_fail_unknown_property_set_id();
452 return AL_NO_ERROR;
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)
488 return;
490 mExtensions.emplace(mExtensions.begin(), "EAX-RAM"sv);
491 if(eaxIsCapable())
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)
504 return;
506 if(mEaxIsTried)
507 eax_fail("No EAX.");
509 mEaxIsTried = true;
511 if(!eax_g_is_enabled)
512 eax_fail("EAX disabled by a configuration.");
514 eax_ensure_compatibility();
515 eax_set_defaults();
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;
557 case DevFmtStereo:
558 /* Pretend 7.1 if using UHJ output, since they both provide full
559 * horizontal surround.
561 if(mDevice->mUhjEncoder)
562 return SPEAKERS_7;
563 if(mDevice->Flags.test(DirectEar))
564 return HEADPHONES;
565 return SPEAKERS_2;
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);
586 return HEADPHONES;
588 #undef EAX_PREFIX
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);
618 state.d = 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);
633 state.d = 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);
641 mEax = mEax5.i;
642 mEaxVersion = 5;
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:
684 break;
685 case EAXCONTEXT_LASTERROR:
686 call.set_value<ContextException>(mEaxLastError);
687 mEaxLastError = EAX_OK;
688 break;
689 case EAXCONTEXT_SPEAKERCONFIG:
690 call.set_value<ContextException>(mEaxSpeakerConfig);
691 break;
692 case EAXCONTEXT_EAXSESSION:
693 call.set_value<ContextException>(mEaxSession);
694 break;
695 default:
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);
706 break;
707 case EAXCONTEXT_PRIMARYFXSLOTID:
708 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
709 break;
710 case EAXCONTEXT_DISTANCEFACTOR:
711 call.set_value<ContextException>(props.flDistanceFactor);
712 break;
713 case EAXCONTEXT_AIRABSORPTIONHF:
714 call.set_value<ContextException>(props.flAirAbsorptionHF);
715 break;
716 case EAXCONTEXT_HFREFERENCE:
717 call.set_value<ContextException>(props.flHFReference);
718 break;
719 default:
720 eax_get_misc(call);
721 break;
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);
731 break;
732 case EAXCONTEXT_PRIMARYFXSLOTID:
733 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
734 break;
735 case EAXCONTEXT_DISTANCEFACTOR:
736 call.set_value<ContextException>(props.flDistanceFactor);
737 break;
738 case EAXCONTEXT_AIRABSORPTIONHF:
739 call.set_value<ContextException>(props.flAirAbsorptionHF);
740 break;
741 case EAXCONTEXT_HFREFERENCE:
742 call.set_value<ContextException>(props.flHFReference);
743 break;
744 case EAXCONTEXT_MACROFXFACTOR:
745 call.set_value<ContextException>(props.flMacroFXFactor);
746 break;
747 default:
748 eax_get_misc(call);
749 break;
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. */
771 mPropsDirty = true;
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)
779 return;
781 mAirAbsorptionGainHF = new_value;
782 mPropsDirty = true;
785 void ALCcontext::eax_context_commit_hf_reference()
787 // TODO
790 void ALCcontext::eax_context_commit_macro_fx_factor()
792 // TODO
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:
814 break;
815 case EAXCONTEXT_SPEAKERCONFIG:
816 eax_set<Eax5SpeakerConfigValidator>(call, mEaxSpeakerConfig);
817 break;
818 case EAXCONTEXT_EAXSESSION:
819 eax_set<Eax5SessionAllValidator>(call, mEaxSession);
820 break;
821 default:
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;
832 dst_d = src;
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);
853 break;
854 case EAXCONTEXT_PRIMARYFXSLOTID:
855 eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
856 call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
857 break;
858 case EAXCONTEXT_DISTANCEFACTOR:
859 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
860 call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
861 break;
862 case EAXCONTEXT_AIRABSORPTIONHF:
863 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
864 call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
865 break;
866 case EAXCONTEXT_HFREFERENCE:
867 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
868 call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
869 break;
870 default:
871 eax_set_misc(call);
872 break;
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;
882 dst_d = src;
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);
906 break;
907 case EAXCONTEXT_PRIMARYFXSLOTID:
908 eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
909 call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
910 break;
911 case EAXCONTEXT_DISTANCEFACTOR:
912 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
913 call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
914 break;
915 case EAXCONTEXT_AIRABSORPTIONHF:
916 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
917 call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
918 break;
919 case EAXCONTEXT_HFREFERENCE:
920 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
921 call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
922 break;
923 case EAXCONTEXT_MACROFXFACTOR:
924 eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
925 call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
926 break;
927 default:
928 eax_set_misc(call);
929 break;
933 void ALCcontext::eax_set(const EaxCall& call)
935 const auto version = call.get_version();
936 switch(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{})
950 return;
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{})
967 return;
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{};
987 switch(mEaxVersion)
989 case 1:
990 case 2:
991 case 3:
992 eax5_context_commit(mEax123, dst_df);
993 break;
994 case 4:
995 eax4_context_commit(mEax4, dst_df);
996 break;
997 case 5:
998 eax5_context_commit(mEax5, dst_df);
999 break;
1002 if(dst_df == EaxDirtyFlags{})
1003 return;
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();
1028 eaxCommitFxSlots();
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);
1048 catch(...)
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);
1071 catch(...)
1073 context->eaxSetLastError();
1074 eax_log_exception(std::data(__func__));
1075 return AL_INVALID_OPERATION;
1077 #endif // ALSOFT_EAX