Rename a potentially confusing member function
[openal-soft.git] / alc / context.cpp
blob906a160eb2ae9a204e462ff8148159ee0ea7529d
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<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
89 thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
90 ALCcontext::ThreadCtx::~ThreadCtx()
92 if(ALCcontext *ctx{ALCcontext::sLocalContext})
94 const bool result{ctx->releaseIfNoDelete()};
95 ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
96 result ? "" : ", leak detected");
99 thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
101 ALeffect ALCcontext::sDefaultEffect;
104 #ifdef __MINGW32__
105 ALCcontext *ALCcontext::getThreadContext() noexcept
106 { return sLocalContext; }
107 void ALCcontext::setThreadContext(ALCcontext *context) noexcept
108 { sThreadContext.set(context); }
109 #endif
111 ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
112 : ContextBase{device.get()}, mALDevice{std::move(device)}
116 ALCcontext::~ALCcontext()
118 TRACE("Freeing context %p\n", voidp{this});
120 size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u},
121 [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
122 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
123 if(count > 0)
124 WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
125 mSourceList.clear();
126 mNumSources = 0;
128 #ifdef ALSOFT_EAX
129 eax_uninitialize();
130 #endif // ALSOFT_EAX
132 mDefaultSlot = nullptr;
133 count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
134 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
135 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
136 if(count > 0)
137 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
138 mEffectSlotList.clear();
139 mNumEffectSlots = 0;
142 void ALCcontext::init()
144 if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
146 mDefaultSlot = std::make_unique<ALeffectslot>(this);
147 aluInitEffectPanning(mDefaultSlot->mSlot, this);
150 EffectSlotArray *auxslots;
151 if(!mDefaultSlot)
152 auxslots = EffectSlot::CreatePtrArray(0);
153 else
155 auxslots = EffectSlot::CreatePtrArray(1);
156 (*auxslots)[0] = mDefaultSlot->mSlot;
157 mDefaultSlot->mState = SlotState::Playing;
159 mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
161 allocVoiceChanges();
163 VoiceChange *cur{mVoiceChangeTail};
164 while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
165 cur = next;
166 mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
169 mExtensionList = alExtList;
171 #ifdef ALSOFT_EAX
172 eax_initialize_extensions();
173 #endif // ALSOFT_EAX
175 mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
176 mParams.Matrix = alu::Matrix::Identity();
177 mParams.Velocity = alu::Vector{};
178 mParams.Gain = mListener.Gain;
179 mParams.MetersPerUnit = mListener.mMetersPerUnit;
180 mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
181 mParams.DopplerFactor = mDopplerFactor;
182 mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
183 mParams.SourceDistanceModel = mSourceDistanceModel;
184 mParams.mDistanceModel = mDistanceModel;
187 mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
188 StartEventThrd(this);
191 allocVoices(256);
192 mActiveVoiceCount.store(64, std::memory_order_relaxed);
195 bool ALCcontext::deinit()
197 if(sLocalContext == this)
199 WARN("%p released while current on thread\n", voidp{this});
200 sThreadContext.set(nullptr);
201 dec_ref();
204 ALCcontext *origctx{this};
205 if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
206 dec_ref();
208 bool ret{};
209 /* First make sure this context exists in the device's list. */
210 auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
211 if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
213 using ContextArray = al::FlexArray<ContextBase*>;
214 auto alloc_ctx_array = [](const size_t count) -> ContextArray*
216 if(count == 0) return &DeviceBase::sEmptyContextArray;
217 return ContextArray::Create(count).release();
219 auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
221 /* Copy the current/old context handles to the new array, excluding the
222 * given context.
224 std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
225 std::bind(std::not_equal_to<>{}, _1, this));
227 /* Store the new context array in the device. Wait for any current mix
228 * to finish before deleting the old array.
230 mDevice->mContexts.store(newarray);
231 if(oldarray != &DeviceBase::sEmptyContextArray)
233 mDevice->waitForMix();
234 delete oldarray;
237 ret = !newarray->empty();
239 else
240 ret = !oldarray->empty();
242 StopEventThrd(this);
244 return ret;
247 void ALCcontext::applyAllUpdates()
249 /* Tell the mixer to stop applying updates, then wait for any active
250 * updating to finish, before providing updates.
252 mHoldUpdates.store(true, std::memory_order_release);
253 while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
254 /* busy-wait */
257 #ifdef ALSOFT_EAX
258 if(eax_is_initialized_)
259 eax_commit();
260 #endif
261 if(std::exchange(mPropsDirty, false))
262 UpdateContextProps(this);
263 UpdateAllEffectSlotProps(this);
264 UpdateAllSourceProps(this);
266 /* Now with all updates declared, let the mixer continue applying them so
267 * they all happen at once.
269 mHoldUpdates.store(false, std::memory_order_release);
272 #ifdef ALSOFT_EAX
273 namespace {
275 template<typename F>
276 void ForEachSource(ALCcontext *context, F func)
278 for(auto &sublist : context->mSourceList)
280 uint64_t usemask{~sublist.FreeMask};
281 while(usemask)
283 const int idx{al::countr_zero(usemask)};
284 usemask &= ~(1_u64 << idx);
286 func(sublist.Sources[idx]);
291 } // namespace
294 bool ALCcontext::eax_is_capable() const noexcept
296 return eax_has_enough_aux_sends();
299 void ALCcontext::eax_uninitialize() noexcept
301 if(!eax_is_initialized_)
302 return;
304 eax_is_initialized_ = false;
305 eax_is_tried_ = false;
306 eax_fx_slots_.uninitialize();
309 ALenum ALCcontext::eax_eax_set(
310 const GUID* property_set_id,
311 ALuint property_id,
312 ALuint property_source_id,
313 ALvoid* property_value,
314 ALuint property_value_size)
316 const auto call = create_eax_call(
317 EaxCallType::set,
318 property_set_id,
319 property_id,
320 property_source_id,
321 property_value,
322 property_value_size);
324 const auto eax_version = call.get_version();
325 if(eax_version != eax_version_)
326 eax_df_ = ~EaxDirtyFlags();
327 eax_version_ = eax_version;
328 eax_initialize();
330 switch(call.get_property_set_id())
332 case EaxCallPropertySetId::context:
333 eax_set(call);
334 break;
335 case EaxCallPropertySetId::fx_slot:
336 case EaxCallPropertySetId::fx_slot_effect:
337 eax_dispatch_fx_slot(call);
338 break;
339 case EaxCallPropertySetId::source:
340 eax_dispatch_source(call);
341 break;
342 default:
343 eax_fail_unknown_property_set_id();
346 if(!call.is_deferred() && !mDeferUpdates)
347 applyAllUpdates();
349 return AL_NO_ERROR;
352 ALenum ALCcontext::eax_eax_get(
353 const GUID* property_set_id,
354 ALuint property_id,
355 ALuint property_source_id,
356 ALvoid* property_value,
357 ALuint property_value_size)
359 const auto call = create_eax_call(
360 EaxCallType::get,
361 property_set_id,
362 property_id,
363 property_source_id,
364 property_value,
365 property_value_size);
366 eax_version_ = call.get_version();
367 eax_initialize();
369 switch(call.get_property_set_id())
371 case EaxCallPropertySetId::context:
372 eax_get(call);
373 break;
374 case EaxCallPropertySetId::fx_slot:
375 case EaxCallPropertySetId::fx_slot_effect:
376 eax_dispatch_fx_slot(call);
377 break;
378 case EaxCallPropertySetId::source:
379 eax_dispatch_source(call);
380 break;
381 default:
382 eax_fail_unknown_property_set_id();
385 return AL_NO_ERROR;
388 void ALCcontext::eax_commit_and_update_sources()
390 std::unique_lock<std::mutex> source_lock{mSourceLock};
391 ForEachSource(this, std::mem_fn(&ALsource::eax_commit_and_update));
394 void ALCcontext::eax_set_last_error() noexcept
396 eax_last_error_ = EAXERR_INVALID_OPERATION;
399 [[noreturn]] void ALCcontext::eax_fail(const char* message)
401 throw ContextException{message};
404 [[noreturn]] void ALCcontext::eax_fail_unknown_property_set_id()
406 eax_fail("Unknown property ID.");
409 [[noreturn]] void ALCcontext::eax_fail_unknown_primary_fx_slot_id()
411 eax_fail("Unknown primary FX Slot ID.");
414 [[noreturn]] void ALCcontext::eax_fail_unknown_property_id()
416 eax_fail("Unknown property ID.");
419 [[noreturn]] void ALCcontext::eax_fail_unknown_version()
421 eax_fail("Unknown version.");
424 void ALCcontext::eax_initialize_extensions()
426 if(!eax_g_is_enabled)
427 return;
429 const auto string_max_capacity =
430 std::strlen(mExtensionList) + 1 +
431 std::strlen(eax1_ext_name) + 1 +
432 std::strlen(eax2_ext_name) + 1 +
433 std::strlen(eax3_ext_name) + 1 +
434 std::strlen(eax4_ext_name) + 1 +
435 std::strlen(eax5_ext_name) + 1 +
436 std::strlen(eax_x_ram_ext_name) + 1;
438 eax_extension_list_.reserve(string_max_capacity);
440 if(eax_is_capable()) {
441 eax_extension_list_ += eax1_ext_name;
442 eax_extension_list_ += ' ';
444 eax_extension_list_ += eax2_ext_name;
445 eax_extension_list_ += ' ';
447 eax_extension_list_ += eax3_ext_name;
448 eax_extension_list_ += ' ';
450 eax_extension_list_ += eax4_ext_name;
451 eax_extension_list_ += ' ';
453 eax_extension_list_ += eax5_ext_name;
454 eax_extension_list_ += ' ';
457 eax_extension_list_ += eax_x_ram_ext_name;
458 eax_extension_list_ += ' ';
460 eax_extension_list_ += mExtensionList;
461 mExtensionList = eax_extension_list_.c_str();
464 void ALCcontext::eax_initialize()
466 if(eax_is_initialized_)
467 return;
469 if(eax_is_tried_)
470 eax_fail("No EAX.");
472 eax_is_tried_ = true;
474 if(!eax_g_is_enabled)
475 eax_fail("EAX disabled by a configuration.");
477 eax_ensure_compatibility();
478 eax_set_defaults();
479 eax_context_commit_air_absorbtion_hf();
480 eax_update_speaker_configuration();
481 eax_initialize_fx_slots();
482 eax_initialize_sources();
484 eax_is_initialized_ = true;
485 mPropsDirty = true;
487 if(!mDeferUpdates)
488 applyAllUpdates();
491 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
493 return mDefaultSlot == nullptr;
496 void ALCcontext::eax_ensure_no_default_effect_slot() const
498 if(!eax_has_no_default_effect_slot())
499 eax_fail("There is a default effect slot in the context.");
502 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
504 return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS;
507 void ALCcontext::eax_ensure_enough_aux_sends() const
509 if(!eax_has_enough_aux_sends())
510 eax_fail("Not enough aux sends.");
513 void ALCcontext::eax_ensure_compatibility()
515 eax_ensure_enough_aux_sends();
518 unsigned long ALCcontext::eax_detect_speaker_configuration() const
520 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
522 switch(mDevice->FmtChans)
524 case DevFmtMono: return SPEAKERS_2;
525 case DevFmtStereo:
526 /* Pretend 7.1 if using UHJ output, since they both provide full
527 * horizontal surround.
529 if(mDevice->mUhjEncoder)
530 return SPEAKERS_7;
531 if(mDevice->Flags.test(DirectEar))
532 return HEADPHONES;
533 return SPEAKERS_2;
534 case DevFmtQuad: return SPEAKERS_4;
535 case DevFmtX51: return SPEAKERS_5;
536 case DevFmtX61: return SPEAKERS_6;
537 case DevFmtX71: return SPEAKERS_7;
538 /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
539 * suggest with-height surround sound (like HRTF).
541 case DevFmtX714: return SPEAKERS_7;
542 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
543 * suggest full-sphere surround sound (like HRTF).
545 case DevFmtX3D71: return SPEAKERS_5;
546 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
547 * provide full-sphere surround sound. Depends if apps are more likely to
548 * consider headphones or 7.1 for surround sound support.
550 case DevFmtAmbi3D: return SPEAKERS_7;
552 ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
553 return HEADPHONES;
555 #undef EAX_PREFIX
558 void ALCcontext::eax_update_speaker_configuration()
560 eax_speaker_config_ = eax_detect_speaker_configuration();
563 void ALCcontext::eax_set_last_error_defaults() noexcept
565 eax_last_error_ = EAX_OK;
568 void ALCcontext::eax_session_set_defaults() noexcept
570 eax_session_.ulEAXVersion = EAXCONTEXT_DEFAULTEAXSESSION;
571 eax_session_.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
574 void ALCcontext::eax4_context_set_defaults(Eax4Props& props) noexcept
576 props.guidPrimaryFXSlotID = EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
577 props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
578 props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
579 props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
582 void ALCcontext::eax4_context_set_defaults(Eax4State& state) noexcept
584 eax4_context_set_defaults(state.i);
585 state.d = state.i;
588 void ALCcontext::eax5_context_set_defaults(Eax5Props& props) noexcept
590 props.guidPrimaryFXSlotID = EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID;
591 props.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
592 props.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
593 props.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
594 props.flMacroFXFactor = EAXCONTEXT_DEFAULTMACROFXFACTOR;
597 void ALCcontext::eax5_context_set_defaults(Eax5State& state) noexcept
599 eax5_context_set_defaults(state.i);
600 state.d = state.i;
603 void ALCcontext::eax4_context_set_current_defaults(const Eax4Props& props) noexcept
605 static_cast<Eax4Props&>(eax_) = props;
606 eax_.flMacroFXFactor = EAXCONTEXT_DEFAULTMACROFXFACTOR;
609 void ALCcontext::eax5_context_set_current_defaults(const Eax5Props& props) noexcept
611 eax_ = props;
614 void ALCcontext::eax_context_set_current_defaults()
616 switch(eax_version_)
618 case 1:
619 case 2:
620 case 3:
621 eax5_context_set_current_defaults(eax123_.i);
622 break;
623 case 4:
624 eax4_context_set_current_defaults(eax4_.i);
625 break;
626 case 5:
627 eax5_context_set_current_defaults(eax5_.i);
628 break;
629 default:
630 eax_fail_unknown_version();
633 eax_df_ = ~EaxDirtyFlags{};
636 void ALCcontext::eax_context_set_defaults()
638 eax5_context_set_defaults(eax123_);
639 eax4_context_set_defaults(eax4_);
640 eax5_context_set_defaults(eax5_);
641 eax_context_set_current_defaults();
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 = eax_get_fx_slot(*fx_slot_index);
658 if(fx_slot.eax_dispatch(call))
660 std::lock_guard<std::mutex> source_lock{mSourceLock};
661 ForEachSource(this, [](ALsource& source){ source.eax_mark_as_changed(); });
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::eax_lookup_source(*this, source_id);
671 if (source == nullptr)
672 eax_fail("Source not found.");
674 source->eax_dispatch(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>(eax_last_error_);
685 break;
686 case EAXCONTEXT_SPEAKERCONFIG:
687 call.set_value<ContextException>(eax_speaker_config_);
688 break;
689 case EAXCONTEXT_EAXSESSION:
690 call.set_value<ContextException>(eax_session_);
691 break;
692 default:
693 eax_fail_unknown_property_id();
697 void ALCcontext::eax4_get(const EaxCall& call, const Eax4Props& props)
699 switch(call.get_property_id())
701 case EAXCONTEXT_ALLPARAMETERS:
702 call.set_value<ContextException>(props);
703 break;
704 case EAXCONTEXT_PRIMARYFXSLOTID:
705 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
706 break;
707 case EAXCONTEXT_DISTANCEFACTOR:
708 call.set_value<ContextException>(props.flDistanceFactor);
709 break;
710 case EAXCONTEXT_AIRABSORPTIONHF:
711 call.set_value<ContextException>(props.flAirAbsorptionHF);
712 break;
713 case EAXCONTEXT_HFREFERENCE:
714 call.set_value<ContextException>(props.flHFReference);
715 break;
716 default:
717 eax_get_misc(call);
718 break;
722 void ALCcontext::eax5_get(const EaxCall& call, const Eax5Props& props)
724 switch(call.get_property_id())
726 case EAXCONTEXT_ALLPARAMETERS:
727 call.set_value<ContextException>(props);
728 break;
729 case EAXCONTEXT_PRIMARYFXSLOTID:
730 call.set_value<ContextException>(props.guidPrimaryFXSlotID);
731 break;
732 case EAXCONTEXT_DISTANCEFACTOR:
733 call.set_value<ContextException>(props.flDistanceFactor);
734 break;
735 case EAXCONTEXT_AIRABSORPTIONHF:
736 call.set_value<ContextException>(props.flAirAbsorptionHF);
737 break;
738 case EAXCONTEXT_HFREFERENCE:
739 call.set_value<ContextException>(props.flHFReference);
740 break;
741 case EAXCONTEXT_MACROFXFACTOR:
742 call.set_value<ContextException>(props.flMacroFXFactor);
743 break;
744 default:
745 eax_get_misc(call);
746 break;
750 void ALCcontext::eax_get(const EaxCall& call)
752 switch(call.get_version())
754 case 4: eax4_get(call, eax4_.i); break;
755 case 5: eax5_get(call, eax5_.i); break;
756 default: eax_fail_unknown_version();
760 void ALCcontext::eax_context_commit_primary_fx_slot_id()
762 eax_primary_fx_slot_index_ = eax_.guidPrimaryFXSlotID;
765 void ALCcontext::eax_context_commit_distance_factor()
767 if(mListener.mMetersPerUnit == eax_.flDistanceFactor)
768 return;
770 mListener.mMetersPerUnit = eax_.flDistanceFactor;
771 mPropsDirty = true;
774 void ALCcontext::eax_context_commit_air_absorbtion_hf()
776 const auto new_value = level_mb_to_gain(eax_.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 eax_fx_slots_.initialize(*this);
798 eax_primary_fx_slot_index_ = eax_.guidPrimaryFXSlotID;
801 void ALCcontext::eax_initialize_sources()
803 std::unique_lock<std::mutex> source_lock{mSourceLock};
804 auto init_source = [this](ALsource &source) noexcept
805 { source.eax_initialize(this); };
806 ForEachSource(this, init_source);
809 void ALCcontext::eax_update_sources()
811 std::unique_lock<std::mutex> source_lock{mSourceLock};
812 auto update_source = [](ALsource &source)
813 { source.eax_commit(); };
814 ForEachSource(this, update_source);
817 void ALCcontext::eax_set_misc(const EaxCall& call)
819 switch(call.get_property_id())
821 case EAXCONTEXT_NONE:
822 break;
823 case EAXCONTEXT_SPEAKERCONFIG:
824 eax_set<Eax5SpeakerConfigValidator>(call, eax_speaker_config_);
825 break;
826 case EAXCONTEXT_EAXSESSION:
827 eax_set<Eax5SessionAllValidator>(call, eax_session_);
828 break;
829 default:
830 eax_fail_unknown_property_id();
834 void ALCcontext::eax4_defer_all(const EaxCall& call, Eax4State& state)
836 const auto& src = call.get_value<ContextException, const EAX40CONTEXTPROPERTIES>();
837 Eax4AllValidator{}(src);
838 const auto& dst_i = state.i;
839 auto& dst_d = state.d;
840 dst_d = src;
842 if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
843 eax_df_ |= eax_primary_fx_slot_id_dirty_bit;
845 if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
846 eax_df_ |= eax_distance_factor_dirty_bit;
848 if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
849 eax_df_ |= eax_air_absorption_hf_dirty_bit;
851 if(dst_i.flHFReference != dst_d.flHFReference)
852 eax_df_ |= eax_hf_reference_dirty_bit;
855 void ALCcontext::eax4_defer(const EaxCall& call, Eax4State& state)
857 switch(call.get_property_id())
859 case EAXCONTEXT_ALLPARAMETERS:
860 eax4_defer_all(call, state);
861 break;
862 case EAXCONTEXT_PRIMARYFXSLOTID:
863 eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
864 call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
865 break;
866 case EAXCONTEXT_DISTANCEFACTOR:
867 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
868 call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
869 break;
870 case EAXCONTEXT_AIRABSORPTIONHF:
871 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
872 call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
873 break;
874 case EAXCONTEXT_HFREFERENCE:
875 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
876 call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
877 break;
878 default:
879 eax_set_misc(call);
880 break;
884 void ALCcontext::eax5_defer_all(const EaxCall& call, Eax5State& state)
886 const auto& src = call.get_value<ContextException, const EAX50CONTEXTPROPERTIES>();
887 Eax4AllValidator{}(src);
888 const auto& dst_i = state.i;
889 auto& dst_d = state.d;
890 dst_d = src;
892 if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
893 eax_df_ |= eax_primary_fx_slot_id_dirty_bit;
895 if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
896 eax_df_ |= eax_distance_factor_dirty_bit;
898 if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
899 eax_df_ |= eax_air_absorption_hf_dirty_bit;
901 if(dst_i.flHFReference != dst_d.flHFReference)
902 eax_df_ |= eax_hf_reference_dirty_bit;
904 if(dst_i.flMacroFXFactor != dst_d.flMacroFXFactor)
905 eax_df_ |= eax_macro_fx_factor_dirty_bit;
908 void ALCcontext::eax5_defer(const EaxCall& call, Eax5State& state)
910 switch(call.get_property_id())
912 case EAXCONTEXT_ALLPARAMETERS:
913 eax5_defer_all(call, state);
914 break;
915 case EAXCONTEXT_PRIMARYFXSLOTID:
916 eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
917 call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
918 break;
919 case EAXCONTEXT_DISTANCEFACTOR:
920 eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
921 call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
922 break;
923 case EAXCONTEXT_AIRABSORPTIONHF:
924 eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
925 call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
926 break;
927 case EAXCONTEXT_HFREFERENCE:
928 eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
929 call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
930 break;
931 case EAXCONTEXT_MACROFXFACTOR:
932 eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
933 call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
934 break;
935 default:
936 eax_set_misc(call);
937 break;
941 void ALCcontext::eax_set(const EaxCall& call)
943 switch(call.get_version())
945 case 4: eax4_defer(call, eax4_); break;
946 case 5: eax5_defer(call, eax5_); break;
947 default: eax_fail_unknown_version();
951 void ALCcontext::eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df)
953 if(eax_df_ == EaxDirtyFlags{})
954 return;
956 eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
957 state, dst_df, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
958 eax_context_commit_property<eax_distance_factor_dirty_bit>(
959 state, dst_df, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
960 eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
961 state, dst_df, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
962 eax_context_commit_property<eax_hf_reference_dirty_bit>(
963 state, dst_df, &EAX40CONTEXTPROPERTIES::flHFReference);
965 eax_df_ = EaxDirtyFlags{};
968 void ALCcontext::eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df)
970 if(eax_df_ == EaxDirtyFlags{})
971 return;
973 eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
974 state, dst_df, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
975 eax_context_commit_property<eax_distance_factor_dirty_bit>(
976 state, dst_df, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
977 eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
978 state, dst_df, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
979 eax_context_commit_property<eax_hf_reference_dirty_bit>(
980 state, dst_df, &EAX50CONTEXTPROPERTIES::flHFReference);
981 eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(
982 state, dst_df, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
984 eax_df_ = EaxDirtyFlags{};
987 void ALCcontext::eax_context_commit()
989 auto dst_df = EaxDirtyFlags{};
991 switch(eax_version_)
993 case 1:
994 case 2:
995 case 3:
996 eax5_context_commit(eax123_, dst_df);
997 break;
998 case 4:
999 eax4_context_commit(eax4_, dst_df);
1000 break;
1001 case 5:
1002 eax5_context_commit(eax5_, dst_df);
1003 break;
1004 default:
1005 eax_fail_unknown_version();
1008 if(dst_df == EaxDirtyFlags{})
1009 return;
1011 if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
1012 eax_context_commit_primary_fx_slot_id();
1014 if((dst_df & eax_distance_factor_dirty_bit) != EaxDirtyFlags{})
1015 eax_context_commit_distance_factor();
1017 if((dst_df & eax_air_absorption_hf_dirty_bit) != EaxDirtyFlags{})
1018 eax_context_commit_air_absorbtion_hf();
1020 if((dst_df & eax_hf_reference_dirty_bit) != EaxDirtyFlags{})
1021 eax_context_commit_hf_reference();
1023 if((dst_df & eax_macro_fx_factor_dirty_bit) != EaxDirtyFlags{})
1024 eax_context_commit_macro_fx_factor();
1026 if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
1027 eax_update_sources();
1030 void ALCcontext::eax_commit()
1032 eax_context_commit();
1035 namespace {
1037 class EaxSetException : public EaxException {
1038 public:
1039 explicit EaxSetException(const char* message)
1040 : EaxException{"EAX_SET", message}
1044 [[noreturn]] void eax_fail_set(const char* message)
1046 throw EaxSetException{message};
1049 class EaxGetException : public EaxException {
1050 public:
1051 explicit EaxGetException(const char* message)
1052 : EaxException{"EAX_GET", message}
1056 [[noreturn]] void eax_fail_get(const char* message)
1058 throw EaxGetException{message};
1061 } // namespace
1064 FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
1065 const GUID* property_set_id,
1066 ALuint property_id,
1067 ALuint property_source_id,
1068 ALvoid* property_value,
1069 ALuint property_value_size) noexcept
1072 auto context = GetContextRef();
1074 if(!context)
1075 eax_fail_set("No current context.");
1077 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1079 return context->eax_eax_set(
1080 property_set_id,
1081 property_id,
1082 property_source_id,
1083 property_value,
1084 property_value_size);
1086 catch (...)
1088 eax_log_exception(__func__);
1089 return AL_INVALID_OPERATION;
1092 FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
1093 const GUID* property_set_id,
1094 ALuint property_id,
1095 ALuint property_source_id,
1096 ALvoid* property_value,
1097 ALuint property_value_size) noexcept
1100 auto context = GetContextRef();
1102 if(!context)
1103 eax_fail_get("No current context.");
1105 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1107 return context->eax_eax_get(
1108 property_set_id,
1109 property_id,
1110 property_source_id,
1111 property_value,
1112 property_value_size);
1114 catch (...)
1116 eax_log_exception(__func__);
1117 return AL_INVALID_OPERATION;
1119 #endif // ALSOFT_EAX