Don't return a large-ish array on the stack
[openal-soft.git] / alc / context.cpp
blobb1b95321dcaad991a0ab0702a8deb91c4def1168
2 #include "config.h"
4 #include "context.h"
6 #include <algorithm>
7 #include <functional>
8 #include <limits>
9 #include <numeric>
10 #include <stddef.h>
11 #include <stdexcept>
13 #include "AL/efx.h"
15 #include "al/auxeffectslot.h"
16 #include "al/source.h"
17 #include "al/effect.h"
18 #include "al/event.h"
19 #include "al/listener.h"
20 #include "albit.h"
21 #include "alc/alu.h"
22 #include "core/async_event.h"
23 #include "core/device.h"
24 #include "core/effectslot.h"
25 #include "core/logging.h"
26 #include "core/voice.h"
27 #include "core/voice_change.h"
28 #include "device.h"
29 #include "ringbuffer.h"
30 #include "vecmat.h"
32 #ifdef ALSOFT_EAX
33 #include <cstring>
34 #include "alstring.h"
35 #include "al/eax/globals.h"
36 #endif // ALSOFT_EAX
38 namespace {
40 using namespace std::placeholders;
42 using voidp = void*;
44 /* Default context extensions */
45 constexpr ALchar alExtList[] =
46 "AL_EXT_ALAW "
47 "AL_EXT_BFORMAT "
48 "AL_EXT_DOUBLE "
49 "AL_EXT_EXPONENT_DISTANCE "
50 "AL_EXT_FLOAT32 "
51 "AL_EXT_IMA4 "
52 "AL_EXT_LINEAR_DISTANCE "
53 "AL_EXT_MCFORMATS "
54 "AL_EXT_MULAW "
55 "AL_EXT_MULAW_BFORMAT "
56 "AL_EXT_MULAW_MCFORMATS "
57 "AL_EXT_OFFSET "
58 "AL_EXT_source_distance_model "
59 "AL_EXT_SOURCE_RADIUS "
60 "AL_EXT_STEREO_ANGLES "
61 "AL_LOKI_quadriphonic "
62 "AL_SOFT_bformat_ex "
63 "AL_SOFTX_bformat_hoa "
64 "AL_SOFT_block_alignment "
65 "AL_SOFT_callback_buffer "
66 "AL_SOFTX_convolution_reverb "
67 "AL_SOFT_deferred_updates "
68 "AL_SOFT_direct_channels "
69 "AL_SOFT_direct_channels_remix "
70 "AL_SOFT_effect_target "
71 "AL_SOFT_events "
72 "AL_SOFT_gain_clamp_ex "
73 "AL_SOFTX_hold_on_disconnect "
74 "AL_SOFT_loop_points "
75 "AL_SOFTX_map_buffer "
76 "AL_SOFT_MSADPCM "
77 "AL_SOFT_source_latency "
78 "AL_SOFT_source_length "
79 "AL_SOFT_source_resampler "
80 "AL_SOFT_source_spatialize "
81 "AL_SOFTX_source_start_delay "
82 "AL_SOFT_UHJ";
84 } // namespace
87 std::atomic<bool> ALCcontext::sGlobalContextLock{false};
88 std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
90 thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
91 ALCcontext::ThreadCtx::~ThreadCtx()
93 if(ALCcontext *ctx{ALCcontext::sLocalContext})
95 const bool result{ctx->releaseIfNoDelete()};
96 ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
97 result ? "" : ", leak detected");
100 thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
102 ALeffect ALCcontext::sDefaultEffect;
105 #ifdef __MINGW32__
106 ALCcontext *ALCcontext::getThreadContext() noexcept
107 { return sLocalContext; }
108 void ALCcontext::setThreadContext(ALCcontext *context) noexcept
109 { sThreadContext.set(context); }
110 #endif
112 ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
113 : ContextBase{device.get()}, mALDevice{std::move(device)}
117 ALCcontext::~ALCcontext()
119 TRACE("Freeing context %p\n", voidp{this});
121 size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u},
122 [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
123 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
124 if(count > 0)
125 WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
126 mSourceList.clear();
127 mNumSources = 0;
129 #ifdef ALSOFT_EAX
130 eax_uninitialize();
131 #endif // ALSOFT_EAX
133 mDefaultSlot = nullptr;
134 count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
135 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
136 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
137 if(count > 0)
138 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
139 mEffectSlotList.clear();
140 mNumEffectSlots = 0;
143 void ALCcontext::init()
145 if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
147 mDefaultSlot = std::make_unique<ALeffectslot>(this);
148 aluInitEffectPanning(mDefaultSlot->mSlot, this);
151 EffectSlotArray *auxslots;
152 if(!mDefaultSlot)
153 auxslots = EffectSlot::CreatePtrArray(0);
154 else
156 auxslots = EffectSlot::CreatePtrArray(1);
157 (*auxslots)[0] = mDefaultSlot->mSlot;
158 mDefaultSlot->mState = SlotState::Playing;
160 mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
162 allocVoiceChanges();
164 VoiceChange *cur{mVoiceChangeTail};
165 while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
166 cur = next;
167 mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
170 mExtensionList = alExtList;
172 #ifdef ALSOFT_EAX
173 eax_initialize_extensions();
174 #endif // ALSOFT_EAX
176 mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
177 mParams.Matrix = alu::Matrix::Identity();
178 mParams.Velocity = alu::Vector{};
179 mParams.Gain = mListener.Gain;
180 mParams.MetersPerUnit = mListener.mMetersPerUnit;
181 mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
182 mParams.DopplerFactor = mDopplerFactor;
183 mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
184 mParams.SourceDistanceModel = mSourceDistanceModel;
185 mParams.mDistanceModel = mDistanceModel;
188 mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
189 StartEventThrd(this);
192 allocVoices(256);
193 mActiveVoiceCount.store(64, std::memory_order_relaxed);
196 bool ALCcontext::deinit()
198 if(sLocalContext == this)
200 WARN("%p released while current on thread\n", voidp{this});
201 sThreadContext.set(nullptr);
202 dec_ref();
205 ALCcontext *origctx{this};
206 if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
208 while(sGlobalContextLock.load()) {
209 /* Wait to make sure another thread didn't get the context and is
210 * trying to increment its refcount.
213 dec_ref();
216 bool ret{};
217 /* First make sure this context exists in the device's list. */
218 auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
219 if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
221 using ContextArray = al::FlexArray<ContextBase*>;
222 auto alloc_ctx_array = [](const size_t count) -> ContextArray*
224 if(count == 0) return &DeviceBase::sEmptyContextArray;
225 return ContextArray::Create(count).release();
227 auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
229 /* Copy the current/old context handles to the new array, excluding the
230 * given context.
232 std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
233 [this](auto a){ return a != this; });
235 /* Store the new context array in the device. Wait for any current mix
236 * to finish before deleting the old array.
238 mDevice->mContexts.store(newarray);
239 if(oldarray != &DeviceBase::sEmptyContextArray)
241 mDevice->waitForMix();
242 delete oldarray;
245 ret = !newarray->empty();
247 else
248 ret = !oldarray->empty();
250 StopEventThrd(this);
252 return ret;
255 void ALCcontext::applyAllUpdates()
257 /* Tell the mixer to stop applying updates, then wait for any active
258 * updating to finish, before providing updates.
260 mHoldUpdates.store(true, std::memory_order_release);
261 while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
262 /* busy-wait */
265 #ifdef ALSOFT_EAX
266 if(eax_is_initialized_)
267 eax_commit();
268 #endif
269 if(std::exchange(mPropsDirty, false))
270 UpdateContextProps(this);
271 UpdateAllEffectSlotProps(this);
272 UpdateAllSourceProps(this);
274 /* Now with all updates declared, let the mixer continue applying them so
275 * they all happen at once.
277 mHoldUpdates.store(false, std::memory_order_release);
280 #ifdef ALSOFT_EAX
281 namespace {
283 template<typename F>
284 void ForEachSource(ALCcontext *context, F func)
286 for(auto &sublist : context->mSourceList)
288 uint64_t usemask{~sublist.FreeMask};
289 while(usemask)
291 const int idx{al::countr_zero(usemask)};
292 usemask &= ~(1_u64 << idx);
294 func(sublist.Sources[idx]);
299 } // namespace
302 bool ALCcontext::eax_is_capable() const noexcept
304 return eax_has_enough_aux_sends();
307 void ALCcontext::eax_uninitialize() noexcept
309 if(!eax_is_initialized_)
310 return;
312 eax_is_initialized_ = false;
313 eax_is_tried_ = false;
314 eax_fx_slots_.uninitialize();
317 ALenum ALCcontext::eax_eax_set(
318 const GUID* property_set_id,
319 ALuint property_id,
320 ALuint property_source_id,
321 ALvoid* property_value,
322 ALuint property_value_size)
324 const auto call = create_eax_call(
325 EaxCallType::set,
326 property_set_id,
327 property_id,
328 property_source_id,
329 property_value,
330 property_value_size);
332 const auto eax_version = call.get_version();
333 if(eax_version != eax_version_)
334 eax_df_ = ~EaxDirtyFlags();
335 eax_version_ = eax_version;
336 eax_initialize();
338 switch(call.get_property_set_id())
340 case EaxCallPropertySetId::context:
341 eax_set(call);
342 break;
343 case EaxCallPropertySetId::fx_slot:
344 case EaxCallPropertySetId::fx_slot_effect:
345 eax_dispatch_fx_slot(call);
346 break;
347 case EaxCallPropertySetId::source:
348 eax_dispatch_source(call);
349 break;
350 default:
351 eax_fail_unknown_property_set_id();
354 if(!call.is_deferred() && !mDeferUpdates)
355 applyAllUpdates();
357 return AL_NO_ERROR;
360 ALenum ALCcontext::eax_eax_get(
361 const GUID* property_set_id,
362 ALuint property_id,
363 ALuint property_source_id,
364 ALvoid* property_value,
365 ALuint property_value_size)
367 const auto call = create_eax_call(
368 EaxCallType::get,
369 property_set_id,
370 property_id,
371 property_source_id,
372 property_value,
373 property_value_size);
374 eax_version_ = call.get_version();
375 eax_initialize();
377 switch(call.get_property_set_id())
379 case EaxCallPropertySetId::context:
380 eax_get(call);
381 break;
382 case EaxCallPropertySetId::fx_slot:
383 case EaxCallPropertySetId::fx_slot_effect:
384 eax_dispatch_fx_slot(call);
385 break;
386 case EaxCallPropertySetId::source:
387 eax_dispatch_source(call);
388 break;
389 default:
390 eax_fail_unknown_property_set_id();
393 return AL_NO_ERROR;
396 void ALCcontext::eax_commit_and_update_sources()
398 std::unique_lock<std::mutex> source_lock{mSourceLock};
399 ForEachSource(this, std::mem_fn(&ALsource::eax_commit_and_update));
402 void ALCcontext::eax_set_last_error() noexcept
404 eax_last_error_ = EAXERR_INVALID_OPERATION;
407 [[noreturn]] void ALCcontext::eax_fail(const char* message)
409 throw ContextException{message};
412 [[noreturn]] void ALCcontext::eax_fail_unknown_property_set_id()
414 eax_fail("Unknown property ID.");
417 [[noreturn]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
419 eax_fail("Unknown primary FX Slot ID.");
422 [[noreturn]] void ALCcontext::eax_fail_unknown_property_id()
424 eax_fail("Unknown property ID.");
427 [[noreturn]] void ALCcontext::eax_fail_unknown_version()
429 eax_fail("Unknown version.");
432 void ALCcontext::eax_initialize_extensions()
434 if(!eax_g_is_enabled)
435 return;
437 const auto string_max_capacity =
438 std::strlen(mExtensionList) + 1 +
439 std::strlen(eax1_ext_name) + 1 +
440 std::strlen(eax2_ext_name) + 1 +
441 std::strlen(eax3_ext_name) + 1 +
442 std::strlen(eax4_ext_name) + 1 +
443 std::strlen(eax5_ext_name) + 1 +
444 std::strlen(eax_x_ram_ext_name) + 1;
446 eax_extension_list_.reserve(string_max_capacity);
448 if(eax_is_capable()) {
449 eax_extension_list_ += eax1_ext_name;
450 eax_extension_list_ += ' ';
452 eax_extension_list_ += eax2_ext_name;
453 eax_extension_list_ += ' ';
455 eax_extension_list_ += eax3_ext_name;
456 eax_extension_list_ += ' ';
458 eax_extension_list_ += eax4_ext_name;
459 eax_extension_list_ += ' ';
461 eax_extension_list_ += eax5_ext_name;
462 eax_extension_list_ += ' ';
465 eax_extension_list_ += eax_x_ram_ext_name;
466 eax_extension_list_ += ' ';
468 eax_extension_list_ += mExtensionList;
469 mExtensionList = eax_extension_list_.c_str();
472 void ALCcontext::eax_initialize()
474 if(eax_is_initialized_)
475 return;
477 if(eax_is_tried_)
478 eax_fail("No EAX.");
480 eax_is_tried_ = true;
482 if(!eax_g_is_enabled)
483 eax_fail("EAX disabled by a configuration.");
485 eax_ensure_compatibility();
486 eax_set_defaults();
487 eax_context_commit_air_absorbtion_hf();
488 eax_update_speaker_configuration();
489 eax_initialize_fx_slots();
490 eax_initialize_sources();
492 eax_is_initialized_ = true;
493 mPropsDirty = true;
495 if(!mDeferUpdates)
496 applyAllUpdates();
499 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
501 return mDefaultSlot == nullptr;
504 void ALCcontext::eax_ensure_no_default_effect_slot() const
506 if(!eax_has_no_default_effect_slot())
507 eax_fail("There is a default effect slot in the context.");
510 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
512 return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS;
515 void ALCcontext::eax_ensure_enough_aux_sends() const
517 if(!eax_has_enough_aux_sends())
518 eax_fail("Not enough aux sends.");
521 void ALCcontext::eax_ensure_compatibility()
523 eax_ensure_enough_aux_sends();
526 unsigned long ALCcontext::eax_detect_speaker_configuration() const
528 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
530 switch(mDevice->FmtChans)
532 case DevFmtMono: return SPEAKERS_2;
533 case DevFmtStereo:
534 /* Pretend 7.1 if using UHJ output, since they both provide full
535 * horizontal surround.
537 if(mDevice->mUhjEncoder)
538 return SPEAKERS_7;
539 if(mDevice->Flags.test(DirectEar))
540 return HEADPHONES;
541 return SPEAKERS_2;
542 case DevFmtQuad: return SPEAKERS_4;
543 case DevFmtX51: return SPEAKERS_5;
544 case DevFmtX61: return SPEAKERS_6;
545 case DevFmtX71: return SPEAKERS_7;
546 /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
547 * suggest with-height surround sound (like HRTF).
549 case DevFmtX714: return SPEAKERS_7;
550 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
551 * suggest full-sphere surround sound (like HRTF).
553 case DevFmtX3D71: return SPEAKERS_5;
554 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
555 * provide full-sphere surround sound. Depends if apps are more likely to
556 * consider headphones or 7.1 for surround sound support.
558 case DevFmtAmbi3D: return SPEAKERS_7;
560 ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
561 return HEADPHONES;
563 #undef EAX_PREFIX
566 void ALCcontext::eax_update_speaker_configuration()
568 eax_speaker_config_ = eax_detect_speaker_configuration();
571 void ALCcontext::eax_set_last_error_defaults() noexcept
573 eax_last_error_ = EAX_OK;
576 void ALCcontext::eax_session_set_defaults() noexcept
578 eax_session_.ulEAXVersion = EAXCONTEXT_DEFAULTEAXSESSION;
579 eax_session_.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
582 void ALCcontext::eax4_context_set_defaults(Eax4Props& props) noexcept
584 props.guidPrimaryFXSlotID = EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
585 props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
586 props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
587 props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
590 void ALCcontext::eax4_context_set_defaults(Eax4State& state) noexcept
592 eax4_context_set_defaults(state.i);
593 state.d = state.i;
596 void ALCcontext::eax5_context_set_defaults(Eax5Props& props) noexcept
598 props.guidPrimaryFXSlotID = EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID;
599 props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
600 props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
601 props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
602 props.flMacroFXFactor = EAXCONTEXT_DEFAULTMACROFXFACTOR;
605 void ALCcontext::eax5_context_set_defaults(Eax5State& state) noexcept
607 eax5_context_set_defaults(state.i);
608 state.d = state.i;
611 void ALCcontext::eax4_context_set_current_defaults(const Eax4Props& props) noexcept
613 static_cast<Eax4Props&>(eax_) = props;
614 eax_.flMacroFXFactor = EAXCONTEXT_DEFAULTMACROFXFACTOR;
617 void ALCcontext::eax5_context_set_current_defaults(const Eax5Props& props) noexcept
619 eax_ = props;
622 void ALCcontext::eax_context_set_current_defaults()
624 switch(eax_version_)
626 case 1:
627 case 2:
628 case 3:
629 eax5_context_set_current_defaults(eax123_.i);
630 break;
631 case 4:
632 eax4_context_set_current_defaults(eax4_.i);
633 break;
634 case 5:
635 eax5_context_set_current_defaults(eax5_.i);
636 break;
637 default:
638 eax_fail_unknown_version();
641 eax_df_ = ~EaxDirtyFlags{};
644 void ALCcontext::eax_context_set_defaults()
646 eax5_context_set_defaults(eax123_);
647 eax4_context_set_defaults(eax4_);
648 eax5_context_set_defaults(eax5_);
649 eax_context_set_current_defaults();
652 void ALCcontext::eax_set_defaults()
654 eax_set_last_error_defaults();
655 eax_session_set_defaults();
656 eax_context_set_defaults();
659 void ALCcontext::eax_dispatch_fx_slot(const EaxCall& call)
661 const auto fx_slot_index = call.get_fx_slot_index();
662 if(!fx_slot_index.has_value())
663 eax_fail("Invalid fx slot index.");
665 auto& fx_slot = eax_get_fx_slot(*fx_slot_index);
666 if(fx_slot.eax_dispatch(call))
668 std::lock_guard<std::mutex> source_lock{mSourceLock};
669 ForEachSource(this, [](ALsource& source){ source.eax_mark_as_changed(); });
673 void ALCcontext::eax_dispatch_source(const EaxCall& call)
675 const auto source_id = call.get_property_al_name();
676 std::lock_guard<std::mutex> source_lock{mSourceLock};
677 const auto source = ALsource::eax_lookup_source(*this, source_id);
679 if (source == nullptr)
680 eax_fail("Source not found.");
682 source->eax_dispatch(call);
685 void ALCcontext::eax_get_misc(const EaxCall& call)
687 switch(call.get_property_id())
689 case EAXCONTEXT_NONE:
690 break;
691 case EAXCONTEXT_LASTERROR:
692 call.set_value<ContextException>(eax_last_error_);
693 break;
694 case EAXCONTEXT_SPEAKERCONFIG:
695 call.set_value<ContextException>(eax_speaker_config_);
696 break;
697 case EAXCONTEXT_EAXSESSION:
698 call.set_value<ContextException>(eax_session_);
699 break;
700 default:
701 eax_fail_unknown_property_id();
705 void ALCcontext::eax4_get(const EaxCall& call, const Eax4Props& props)
707 switch(call.get_property_id())
709 case EAXCONTEXT_ALLPARAMETERS:
710 call.set_value<ContextException>(props);
711 break;
712 case EAXCONTEXT_PRIMARYFXSLOTID:
713 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
714 break;
715 case EAXCONTEXT_DISTANCEFACTOR:
716 call.set_value<ContextException>(props.flDistanceFactor);
717 break;
718 case EAXCONTEXT_AIRABSORPTIONHF:
719 call.set_value<ContextException>(props.flAirAbsorptionHF);
720 break;
721 case EAXCONTEXT_HFREFERENCE:
722 call.set_value<ContextException>(props.flHFReference);
723 break;
724 default:
725 eax_get_misc(call);
726 break;
730 void ALCcontext::eax5_get(const EaxCall& call, const Eax5Props& props)
732 switch(call.get_property_id())
734 case EAXCONTEXT_ALLPARAMETERS:
735 call.set_value<ContextException>(props);
736 break;
737 case EAXCONTEXT_PRIMARYFXSLOTID:
738 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
739 break;
740 case EAXCONTEXT_DISTANCEFACTOR:
741 call.set_value<ContextException>(props.flDistanceFactor);
742 break;
743 case EAXCONTEXT_AIRABSORPTIONHF:
744 call.set_value<ContextException>(props.flAirAbsorptionHF);
745 break;
746 case EAXCONTEXT_HFREFERENCE:
747 call.set_value<ContextException>(props.flHFReference);
748 break;
749 case EAXCONTEXT_MACROFXFACTOR:
750 call.set_value<ContextException>(props.flMacroFXFactor);
751 break;
752 default:
753 eax_get_misc(call);
754 break;
758 void ALCcontext::eax_get(const EaxCall& call)
760 switch(call.get_version())
762 case 4: eax4_get(call, eax4_.i); break;
763 case 5: eax5_get(call, eax5_.i); break;
764 default: eax_fail_unknown_version();
768 void ALCcontext::eax_context_commit_primary_fx_slot_id()
770 eax_primary_fx_slot_index_ = eax_.guidPrimaryFXSlotID;
773 void ALCcontext::eax_context_commit_distance_factor()
775 if(mListener.mMetersPerUnit == eax_.flDistanceFactor)
776 return;
778 mListener.mMetersPerUnit = eax_.flDistanceFactor;
779 mPropsDirty = true;
782 void ALCcontext::eax_context_commit_air_absorbtion_hf()
784 const auto new_value = level_mb_to_gain(eax_.flAirAbsorptionHF);
786 if(mAirAbsorptionGainHF == new_value)
787 return;
789 mAirAbsorptionGainHF = new_value;
790 mPropsDirty = true;
793 void ALCcontext::eax_context_commit_hf_reference()
795 // TODO
798 void ALCcontext::eax_context_commit_macro_fx_factor()
800 // TODO
803 void ALCcontext::eax_initialize_fx_slots()
805 eax_fx_slots_.initialize(*this);
806 eax_primary_fx_slot_index_ = eax_.guidPrimaryFXSlotID;
809 void ALCcontext::eax_initialize_sources()
811 std::unique_lock<std::mutex> source_lock{mSourceLock};
812 auto init_source = [this](ALsource &source) noexcept
813 { source.eax_initialize(this); };
814 ForEachSource(this, init_source);
817 void ALCcontext::eax_update_sources()
819 std::unique_lock<std::mutex> source_lock{mSourceLock};
820 auto update_source = [](ALsource &source)
821 { source.eax_commit(); };
822 ForEachSource(this, update_source);
825 void ALCcontext::eax_set_misc(const EaxCall& call)
827 switch(call.get_property_id())
829 case EAXCONTEXT_NONE:
830 break;
831 case EAXCONTEXT_SPEAKERCONFIG:
832 eax_set<Eax5SpeakerConfigValidator>(call, eax_speaker_config_);
833 break;
834 case EAXCONTEXT_EAXSESSION:
835 eax_set<Eax5SessionAllValidator>(call, eax_session_);
836 break;
837 default:
838 eax_fail_unknown_property_id();
842 void ALCcontext::eax4_defer_all(const EaxCall& call, Eax4State& state)
844 const auto& src = call.get_value<ContextException, const EAX40CONTEXTPROPERTIES>();
845 Eax4AllValidator{}(src);
846 const auto& dst_i = state.i;
847 auto& dst_d = state.d;
848 dst_d = src;
850 if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
851 eax_df_ |= eax_primary_fx_slot_id_dirty_bit;
853 if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
854 eax_df_ |= eax_distance_factor_dirty_bit;
856 if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
857 eax_df_ |= eax_air_absorption_hf_dirty_bit;
859 if(dst_i.flHFReference != dst_d.flHFReference)
860 eax_df_ |= eax_hf_reference_dirty_bit;
863 void ALCcontext::eax4_defer(const EaxCall& call, Eax4State& state)
865 switch(call.get_property_id())
867 case EAXCONTEXT_ALLPARAMETERS:
868 eax4_defer_all(call, state);
869 break;
870 case EAXCONTEXT_PRIMARYFXSLOTID:
871 eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
872 call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
873 break;
874 case EAXCONTEXT_DISTANCEFACTOR:
875 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
876 call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
877 break;
878 case EAXCONTEXT_AIRABSORPTIONHF:
879 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
880 call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
881 break;
882 case EAXCONTEXT_HFREFERENCE:
883 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
884 call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
885 break;
886 default:
887 eax_set_misc(call);
888 break;
892 void ALCcontext::eax5_defer_all(const EaxCall& call, Eax5State& state)
894 const auto& src = call.get_value<ContextException, const EAX50CONTEXTPROPERTIES>();
895 Eax4AllValidator{}(src);
896 const auto& dst_i = state.i;
897 auto& dst_d = state.d;
898 dst_d = src;
900 if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
901 eax_df_ |= eax_primary_fx_slot_id_dirty_bit;
903 if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
904 eax_df_ |= eax_distance_factor_dirty_bit;
906 if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
907 eax_df_ |= eax_air_absorption_hf_dirty_bit;
909 if(dst_i.flHFReference != dst_d.flHFReference)
910 eax_df_ |= eax_hf_reference_dirty_bit;
912 if(dst_i.flMacroFXFactor != dst_d.flMacroFXFactor)
913 eax_df_ |= eax_macro_fx_factor_dirty_bit;
916 void ALCcontext::eax5_defer(const EaxCall& call, Eax5State& state)
918 switch(call.get_property_id())
920 case EAXCONTEXT_ALLPARAMETERS:
921 eax5_defer_all(call, state);
922 break;
923 case EAXCONTEXT_PRIMARYFXSLOTID:
924 eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
925 call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
926 break;
927 case EAXCONTEXT_DISTANCEFACTOR:
928 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
929 call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
930 break;
931 case EAXCONTEXT_AIRABSORPTIONHF:
932 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
933 call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
934 break;
935 case EAXCONTEXT_HFREFERENCE:
936 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
937 call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
938 break;
939 case EAXCONTEXT_MACROFXFACTOR:
940 eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
941 call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
942 break;
943 default:
944 eax_set_misc(call);
945 break;
949 void ALCcontext::eax_set(const EaxCall& call)
951 switch(call.get_version())
953 case 4: eax4_defer(call, eax4_); break;
954 case 5: eax5_defer(call, eax5_); break;
955 default: eax_fail_unknown_version();
959 void ALCcontext::eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df)
961 if(eax_df_ == EaxDirtyFlags{})
962 return;
964 eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
965 state, dst_df, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
966 eax_context_commit_property<eax_distance_factor_dirty_bit>(
967 state, dst_df, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
968 eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
969 state, dst_df, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
970 eax_context_commit_property<eax_hf_reference_dirty_bit>(
971 state, dst_df, &EAX40CONTEXTPROPERTIES::flHFReference);
973 eax_df_ = EaxDirtyFlags{};
976 void ALCcontext::eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df)
978 if(eax_df_ == EaxDirtyFlags{})
979 return;
981 eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
982 state, dst_df, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
983 eax_context_commit_property<eax_distance_factor_dirty_bit>(
984 state, dst_df, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
985 eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
986 state, dst_df, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
987 eax_context_commit_property<eax_hf_reference_dirty_bit>(
988 state, dst_df, &EAX50CONTEXTPROPERTIES::flHFReference);
989 eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(
990 state, dst_df, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
992 eax_df_ = EaxDirtyFlags{};
995 void ALCcontext::eax_context_commit()
997 auto dst_df = EaxDirtyFlags{};
999 switch(eax_version_)
1001 case 1:
1002 case 2:
1003 case 3:
1004 eax5_context_commit(eax123_, dst_df);
1005 break;
1006 case 4:
1007 eax4_context_commit(eax4_, dst_df);
1008 break;
1009 case 5:
1010 eax5_context_commit(eax5_, dst_df);
1011 break;
1012 default:
1013 eax_fail_unknown_version();
1016 if(dst_df == EaxDirtyFlags{})
1017 return;
1019 if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
1020 eax_context_commit_primary_fx_slot_id();
1022 if((dst_df & eax_distance_factor_dirty_bit) != EaxDirtyFlags{})
1023 eax_context_commit_distance_factor();
1025 if((dst_df & eax_air_absorption_hf_dirty_bit) != EaxDirtyFlags{})
1026 eax_context_commit_air_absorbtion_hf();
1028 if((dst_df & eax_hf_reference_dirty_bit) != EaxDirtyFlags{})
1029 eax_context_commit_hf_reference();
1031 if((dst_df & eax_macro_fx_factor_dirty_bit) != EaxDirtyFlags{})
1032 eax_context_commit_macro_fx_factor();
1034 if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
1035 eax_update_sources();
1038 void ALCcontext::eax_commit()
1040 eax_context_commit();
1043 namespace {
1045 class EaxSetException : public EaxException {
1046 public:
1047 explicit EaxSetException(const char* message)
1048 : EaxException{"EAX_SET", message}
1052 [[noreturn]] void eax_fail_set(const char* message)
1054 throw EaxSetException{message};
1057 class EaxGetException : public EaxException {
1058 public:
1059 explicit EaxGetException(const char* message)
1060 : EaxException{"EAX_GET", message}
1064 [[noreturn]] void eax_fail_get(const char* message)
1066 throw EaxGetException{message};
1069 } // namespace
1072 FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
1073 const GUID* property_set_id,
1074 ALuint property_id,
1075 ALuint property_source_id,
1076 ALvoid* property_value,
1077 ALuint property_value_size) noexcept
1080 auto context = GetContextRef();
1082 if(!context)
1083 eax_fail_set("No current context.");
1085 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1087 return context->eax_eax_set(
1088 property_set_id,
1089 property_id,
1090 property_source_id,
1091 property_value,
1092 property_value_size);
1094 catch (...)
1096 eax_log_exception(__func__);
1097 return AL_INVALID_OPERATION;
1100 FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
1101 const GUID* property_set_id,
1102 ALuint property_id,
1103 ALuint property_source_id,
1104 ALvoid* property_value,
1105 ALuint property_value_size) noexcept
1108 auto context = GetContextRef();
1110 if(!context)
1111 eax_fail_get("No current context.");
1113 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1115 return context->eax_eax_get(
1116 property_set_id,
1117 property_id,
1118 property_source_id,
1119 property_value,
1120 property_value_size);
1122 catch (...)
1124 eax_log_exception(__func__);
1125 return AL_INVALID_OPERATION;
1127 #endif // ALSOFT_EAX