Move EAX files to their own sub-directory
[openal-soft.git] / alc / context.cpp
blob5fe03e78cbb2c9750aa9e816e7d51474164e839d
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 <cassert>
34 #include <cstring>
36 #include "alstring.h"
37 #include "al/eax/exception.h"
38 #include "al/eax/globals.h"
39 #endif // ALSOFT_EAX
41 namespace {
43 using namespace std::placeholders;
45 using voidp = void*;
47 /* Default context extensions */
48 constexpr ALchar alExtList[] =
49 "AL_EXT_ALAW "
50 "AL_EXT_BFORMAT "
51 "AL_EXT_DOUBLE "
52 "AL_EXT_EXPONENT_DISTANCE "
53 "AL_EXT_FLOAT32 "
54 "AL_EXT_IMA4 "
55 "AL_EXT_LINEAR_DISTANCE "
56 "AL_EXT_MCFORMATS "
57 "AL_EXT_MULAW "
58 "AL_EXT_MULAW_BFORMAT "
59 "AL_EXT_MULAW_MCFORMATS "
60 "AL_EXT_OFFSET "
61 "AL_EXT_source_distance_model "
62 "AL_EXT_SOURCE_RADIUS "
63 "AL_EXT_STEREO_ANGLES "
64 "AL_LOKI_quadriphonic "
65 "AL_SOFT_bformat_ex "
66 "AL_SOFTX_bformat_hoa "
67 "AL_SOFT_block_alignment "
68 "AL_SOFT_callback_buffer "
69 "AL_SOFTX_convolution_reverb "
70 "AL_SOFT_deferred_updates "
71 "AL_SOFT_direct_channels "
72 "AL_SOFT_direct_channels_remix "
73 "AL_SOFT_effect_target "
74 "AL_SOFT_events "
75 "AL_SOFT_gain_clamp_ex "
76 "AL_SOFTX_hold_on_disconnect "
77 "AL_SOFT_loop_points "
78 "AL_SOFTX_map_buffer "
79 "AL_SOFT_MSADPCM "
80 "AL_SOFT_source_latency "
81 "AL_SOFT_source_length "
82 "AL_SOFT_source_resampler "
83 "AL_SOFT_source_spatialize "
84 "AL_SOFT_UHJ";
86 } // namespace
89 std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
91 thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
92 ALCcontext::ThreadCtx::~ThreadCtx()
94 if(ALCcontext *ctx{ALCcontext::sLocalContext})
96 const bool result{ctx->releaseIfNoDelete()};
97 ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
98 result ? "" : ", leak detected");
101 thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
103 ALeffect ALCcontext::sDefaultEffect;
106 #ifdef __MINGW32__
107 ALCcontext *ALCcontext::getThreadContext() noexcept
108 { return sLocalContext; }
109 void ALCcontext::setThreadContext(ALCcontext *context) noexcept
110 { sThreadContext.set(context); }
111 #endif
113 ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
114 : ContextBase{device.get()}, mALDevice{std::move(device)}
118 ALCcontext::~ALCcontext()
120 TRACE("Freeing context %p\n", voidp{this});
122 size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u},
123 [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
124 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
125 if(count > 0)
126 WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
127 mSourceList.clear();
128 mNumSources = 0;
130 #ifdef ALSOFT_EAX
131 eax_uninitialize();
132 #endif // ALSOFT_EAX
134 mDefaultSlot = nullptr;
135 count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
136 [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
137 { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
138 if(count > 0)
139 WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
140 mEffectSlotList.clear();
141 mNumEffectSlots = 0;
144 void ALCcontext::init()
146 if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
148 mDefaultSlot = std::make_unique<ALeffectslot>();
149 aluInitEffectPanning(&mDefaultSlot->mSlot, this);
152 EffectSlotArray *auxslots;
153 if(!mDefaultSlot)
154 auxslots = EffectSlot::CreatePtrArray(0);
155 else
157 auxslots = EffectSlot::CreatePtrArray(1);
158 (*auxslots)[0] = &mDefaultSlot->mSlot;
159 mDefaultSlot->mState = SlotState::Playing;
161 mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
163 allocVoiceChanges();
165 VoiceChange *cur{mVoiceChangeTail};
166 while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
167 cur = next;
168 mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
171 mExtensionList = alExtList;
173 #ifdef ALSOFT_EAX
174 eax_initialize_extensions();
175 #endif // ALSOFT_EAX
177 mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
178 mParams.Matrix = alu::Matrix::Identity();
179 mParams.Velocity = alu::Vector{};
180 mParams.Gain = mListener.Gain;
181 mParams.MetersPerUnit = mListener.mMetersPerUnit;
182 mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
183 mParams.DopplerFactor = mDopplerFactor;
184 mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
185 mParams.SourceDistanceModel = mSourceDistanceModel;
186 mParams.mDistanceModel = mDistanceModel;
189 mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
190 StartEventThrd(this);
193 allocVoices(256);
194 mActiveVoiceCount.store(64, std::memory_order_relaxed);
197 bool ALCcontext::deinit()
199 if(sLocalContext == this)
201 WARN("%p released while current on thread\n", voidp{this});
202 sThreadContext.set(nullptr);
203 release();
206 ALCcontext *origctx{this};
207 if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
208 release();
210 bool ret{};
211 /* First make sure this context exists in the device's list. */
212 auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
213 if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
215 using ContextArray = al::FlexArray<ContextBase*>;
216 auto alloc_ctx_array = [](const size_t count) -> ContextArray*
218 if(count == 0) return &DeviceBase::sEmptyContextArray;
219 return ContextArray::Create(count).release();
221 auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
223 /* Copy the current/old context handles to the new array, excluding the
224 * given context.
226 std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
227 std::bind(std::not_equal_to<>{}, _1, this));
229 /* Store the new context array in the device. Wait for any current mix
230 * to finish before deleting the old array.
232 mDevice->mContexts.store(newarray);
233 if(oldarray != &DeviceBase::sEmptyContextArray)
235 mDevice->waitForMix();
236 delete oldarray;
239 ret = !newarray->empty();
241 else
242 ret = !oldarray->empty();
244 StopEventThrd(this);
246 return ret;
249 void ALCcontext::applyAllUpdates()
251 /* Tell the mixer to stop applying updates, then wait for any active
252 * updating to finish, before providing updates.
254 mHoldUpdates.store(true, std::memory_order_release);
255 while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
256 /* busy-wait */
259 #ifdef ALSOFT_EAX
260 eax_apply_deferred();
261 #endif
262 if(std::exchange(mPropsDirty, false))
263 UpdateContextProps(this);
264 UpdateAllEffectSlotProps(this);
265 UpdateAllSourceProps(this);
267 /* Now with all updates declared, let the mixer continue applying them so
268 * they all happen at once.
270 mHoldUpdates.store(false, std::memory_order_release);
273 #ifdef ALSOFT_EAX
274 namespace {
276 class ContextException :
277 public EaxException
279 public:
280 explicit ContextException(
281 const char* message)
283 EaxException{"EAX_CONTEXT", message}
286 }; // ContextException
289 template<typename F>
290 void ForEachSource(ALCcontext *context, F func)
292 for(auto &sublist : context->mSourceList)
294 uint64_t usemask{~sublist.FreeMask};
295 while(usemask)
297 const int idx{al::countr_zero(usemask)};
298 usemask &= ~(1_u64 << idx);
300 func(sublist.Sources[idx]);
305 } // namespace
308 bool ALCcontext::eax_is_capable() const noexcept
310 return eax_has_enough_aux_sends();
313 void ALCcontext::eax_uninitialize() noexcept
315 if (!eax_is_initialized_)
317 return;
320 eax_is_initialized_ = true;
321 eax_is_tried_ = false;
323 eax_fx_slots_.uninitialize();
326 ALenum ALCcontext::eax_eax_set(
327 const GUID* property_set_id,
328 ALuint property_id,
329 ALuint property_source_id,
330 ALvoid* property_value,
331 ALuint property_value_size)
333 eax_initialize();
335 const auto eax_call = create_eax_call(
336 false,
337 property_set_id,
338 property_id,
339 property_source_id,
340 property_value,
341 property_value_size
344 eax_unlock_legacy_fx_slots(eax_call);
346 switch (eax_call.get_property_set_id())
348 case EaxEaxCallPropertySetId::context:
349 eax_set(eax_call);
350 break;
352 case EaxEaxCallPropertySetId::fx_slot:
353 case EaxEaxCallPropertySetId::fx_slot_effect:
354 eax_dispatch_fx_slot(eax_call);
355 break;
357 case EaxEaxCallPropertySetId::source:
358 eax_dispatch_source(eax_call);
359 break;
361 default:
362 eax_fail("Unsupported property set id.");
365 static constexpr auto deferred_flag = 0x80000000u;
366 if(!(property_id&deferred_flag) && !mDeferUpdates)
367 applyAllUpdates();
369 return AL_NO_ERROR;
372 ALenum ALCcontext::eax_eax_get(
373 const GUID* property_set_id,
374 ALuint property_id,
375 ALuint property_source_id,
376 ALvoid* property_value,
377 ALuint property_value_size)
379 eax_initialize();
381 const auto eax_call = create_eax_call(
382 true,
383 property_set_id,
384 property_id,
385 property_source_id,
386 property_value,
387 property_value_size
390 eax_unlock_legacy_fx_slots(eax_call);
392 switch (eax_call.get_property_set_id())
394 case EaxEaxCallPropertySetId::context:
395 eax_get(eax_call);
396 break;
398 case EaxEaxCallPropertySetId::fx_slot:
399 case EaxEaxCallPropertySetId::fx_slot_effect:
400 eax_dispatch_fx_slot(eax_call);
401 break;
403 case EaxEaxCallPropertySetId::source:
404 eax_dispatch_source(eax_call);
405 break;
407 default:
408 eax_fail("Unsupported property set id.");
411 return AL_NO_ERROR;
414 void ALCcontext::eax_update_filters()
416 ForEachSource(this, std::mem_fn(&ALsource::eax_update_filters));
419 void ALCcontext::eax_commit_and_update_sources()
421 std::unique_lock<std::mutex> source_lock{mSourceLock};
422 ForEachSource(this, std::mem_fn(&ALsource::eax_commit_and_update));
425 void ALCcontext::eax_set_last_error() noexcept
427 eax_last_error_ = EAXERR_INVALID_OPERATION;
430 [[noreturn]]
431 void ALCcontext::eax_fail(
432 const char* message)
434 throw ContextException{message};
437 void ALCcontext::eax_initialize_extensions()
439 if (!eax_g_is_enabled)
441 return;
444 const auto string_max_capacity =
445 std::strlen(mExtensionList) + 1 +
446 std::strlen(eax1_ext_name) + 1 +
447 std::strlen(eax2_ext_name) + 1 +
448 std::strlen(eax3_ext_name) + 1 +
449 std::strlen(eax4_ext_name) + 1 +
450 std::strlen(eax5_ext_name) + 1 +
451 std::strlen(eax_x_ram_ext_name) + 1 +
454 eax_extension_list_.reserve(string_max_capacity);
456 if (eax_is_capable())
458 eax_extension_list_ += eax1_ext_name;
459 eax_extension_list_ += ' ';
461 eax_extension_list_ += eax2_ext_name;
462 eax_extension_list_ += ' ';
464 eax_extension_list_ += eax3_ext_name;
465 eax_extension_list_ += ' ';
467 eax_extension_list_ += eax4_ext_name;
468 eax_extension_list_ += ' ';
470 eax_extension_list_ += eax5_ext_name;
471 eax_extension_list_ += ' ';
474 eax_extension_list_ += eax_x_ram_ext_name;
475 eax_extension_list_ += ' ';
477 eax_extension_list_ += mExtensionList;
478 mExtensionList = eax_extension_list_.c_str();
481 void ALCcontext::eax_initialize()
483 if (eax_is_initialized_)
485 return;
488 if (eax_is_tried_)
490 eax_fail("No EAX.");
493 eax_is_tried_ = true;
495 if (!eax_g_is_enabled)
497 eax_fail("EAX disabled by a configuration.");
500 eax_ensure_compatibility();
501 eax_set_defaults();
502 eax_set_air_absorbtion_hf();
503 eax_update_speaker_configuration();
504 eax_initialize_fx_slots();
505 eax_initialize_sources();
507 eax_is_initialized_ = true;
510 bool ALCcontext::eax_has_no_default_effect_slot() const noexcept
512 return mDefaultSlot == nullptr;
515 void ALCcontext::eax_ensure_no_default_effect_slot() const
517 if (!eax_has_no_default_effect_slot())
519 eax_fail("There is a default effect slot in the context.");
523 bool ALCcontext::eax_has_enough_aux_sends() const noexcept
525 return mALDevice->NumAuxSends >= EAX_MAX_FXSLOTS;
528 void ALCcontext::eax_ensure_enough_aux_sends() const
530 if (!eax_has_enough_aux_sends())
532 eax_fail("Not enough aux sends.");
536 void ALCcontext::eax_ensure_compatibility()
538 eax_ensure_enough_aux_sends();
541 unsigned long ALCcontext::eax_detect_speaker_configuration() const
543 #define EAX_PREFIX "[EAX_DETECT_SPEAKER_CONFIG]"
545 switch(mDevice->FmtChans)
547 case DevFmtMono: return SPEAKERS_2;
548 case DevFmtStereo:
549 /* Pretend 7.1 if using UHJ output, since they both provide full
550 * horizontal surround.
552 if(mDevice->mUhjEncoder)
553 return SPEAKERS_7;
554 if(mDevice->Flags.test(DirectEar))
555 return HEADPHONES;
556 return SPEAKERS_2;
557 case DevFmtQuad: return SPEAKERS_4;
558 case DevFmtX51: return SPEAKERS_5;
559 case DevFmtX61: return SPEAKERS_6;
560 case DevFmtX71: return SPEAKERS_7;
561 /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
562 * suggest full-sphere surround sound (like HRTF).
564 case DevFmtX3D71: return SPEAKERS_5;
565 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
566 * provide full-sphere surround sound. Depends if apps are more likely to
567 * consider headphones or 7.1 for surround sound support.
569 case DevFmtAmbi3D: return SPEAKERS_7;
571 ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
572 return HEADPHONES;
574 #undef EAX_PREFIX
577 void ALCcontext::eax_update_speaker_configuration()
579 eax_speaker_config_ = eax_detect_speaker_configuration();
582 void ALCcontext::eax_set_last_error_defaults() noexcept
584 eax_last_error_ = EAX_OK;
587 void ALCcontext::eax_set_session_defaults() noexcept
589 eax_session_.ulEAXVersion = EAXCONTEXT_MINEAXSESSION;
590 eax_session_.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
593 void ALCcontext::eax_set_context_defaults() noexcept
595 eax_.context.guidPrimaryFXSlotID = EAXCONTEXT_DEFAULTPRIMARYFXSLOTID;
596 eax_.context.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
597 eax_.context.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
598 eax_.context.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
601 void ALCcontext::eax_set_defaults() noexcept
603 eax_set_last_error_defaults();
604 eax_set_session_defaults();
605 eax_set_context_defaults();
607 eax_d_ = eax_;
610 void ALCcontext::eax_unlock_legacy_fx_slots(const EaxEaxCall& eax_call) noexcept
612 if (eax_call.get_version() != 5 || eax_are_legacy_fx_slots_unlocked_)
613 return;
615 eax_are_legacy_fx_slots_unlocked_ = true;
616 eax_fx_slots_.unlock_legacy();
619 void ALCcontext::eax_dispatch_fx_slot(
620 const EaxEaxCall& eax_call)
622 const auto fx_slot_index = eax_call.get_fx_slot_index();
623 if(!fx_slot_index.has_value())
624 eax_fail("Invalid fx slot index.");
626 auto& fx_slot = eax_get_fx_slot(*fx_slot_index);
627 if(fx_slot.eax_dispatch(eax_call))
629 std::lock_guard<std::mutex> source_lock{mSourceLock};
630 eax_update_filters();
634 void ALCcontext::eax_dispatch_source(
635 const EaxEaxCall& eax_call)
637 const auto source_id = eax_call.get_property_al_name();
639 std::lock_guard<std::mutex> source_lock{mSourceLock};
641 const auto source = ALsource::eax_lookup_source(*this, source_id);
643 if (!source)
645 eax_fail("Source not found.");
648 source->eax_dispatch(eax_call);
651 void ALCcontext::eax_get_primary_fx_slot_id(
652 const EaxEaxCall& eax_call)
654 eax_call.set_value<ContextException>(eax_.context.guidPrimaryFXSlotID);
657 void ALCcontext::eax_get_distance_factor(
658 const EaxEaxCall& eax_call)
660 eax_call.set_value<ContextException>(eax_.context.flDistanceFactor);
663 void ALCcontext::eax_get_air_absorption_hf(
664 const EaxEaxCall& eax_call)
666 eax_call.set_value<ContextException>(eax_.context.flAirAbsorptionHF);
669 void ALCcontext::eax_get_hf_reference(
670 const EaxEaxCall& eax_call)
672 eax_call.set_value<ContextException>(eax_.context.flHFReference);
675 void ALCcontext::eax_get_last_error(
676 const EaxEaxCall& eax_call)
678 const auto eax_last_error = eax_last_error_;
679 eax_last_error_ = EAX_OK;
680 eax_call.set_value<ContextException>(eax_last_error);
683 void ALCcontext::eax_get_speaker_config(
684 const EaxEaxCall& eax_call)
686 eax_call.set_value<ContextException>(eax_speaker_config_);
689 void ALCcontext::eax_get_session(
690 const EaxEaxCall& eax_call)
692 eax_call.set_value<ContextException>(eax_session_);
695 void ALCcontext::eax_get_macro_fx_factor(
696 const EaxEaxCall& eax_call)
698 eax_call.set_value<ContextException>(eax_.context.flMacroFXFactor);
701 void ALCcontext::eax_get_context_all(
702 const EaxEaxCall& eax_call)
704 switch (eax_call.get_version())
706 case 4:
707 eax_call.set_value<ContextException>(static_cast<const EAX40CONTEXTPROPERTIES&>(eax_.context));
708 break;
710 case 5:
711 eax_call.set_value<ContextException>(static_cast<const EAX50CONTEXTPROPERTIES&>(eax_.context));
712 break;
714 default:
715 eax_fail("Unsupported EAX version.");
719 void ALCcontext::eax_get(
720 const EaxEaxCall& eax_call)
722 switch (eax_call.get_property_id())
724 case EAXCONTEXT_NONE:
725 break;
727 case EAXCONTEXT_ALLPARAMETERS:
728 eax_get_context_all(eax_call);
729 break;
731 case EAXCONTEXT_PRIMARYFXSLOTID:
732 eax_get_primary_fx_slot_id(eax_call);
733 break;
735 case EAXCONTEXT_DISTANCEFACTOR:
736 eax_get_distance_factor(eax_call);
737 break;
739 case EAXCONTEXT_AIRABSORPTIONHF:
740 eax_get_air_absorption_hf(eax_call);
741 break;
743 case EAXCONTEXT_HFREFERENCE:
744 eax_get_hf_reference(eax_call);
745 break;
747 case EAXCONTEXT_LASTERROR:
748 eax_get_last_error(eax_call);
749 break;
751 case EAXCONTEXT_SPEAKERCONFIG:
752 eax_get_speaker_config(eax_call);
753 break;
755 case EAXCONTEXT_EAXSESSION:
756 eax_get_session(eax_call);
757 break;
759 case EAXCONTEXT_MACROFXFACTOR:
760 eax_get_macro_fx_factor(eax_call);
761 break;
763 default:
764 eax_fail("Unsupported property id.");
768 void ALCcontext::eax_set_primary_fx_slot_id()
770 eax_previous_primary_fx_slot_index_ = eax_primary_fx_slot_index_;
771 eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
774 void ALCcontext::eax_set_distance_factor()
776 mListener.mMetersPerUnit = eax_.context.flDistanceFactor;
777 mPropsDirty = true;
780 void ALCcontext::eax_set_air_absorbtion_hf()
782 mAirAbsorptionGainHF = level_mb_to_gain(eax_.context.flAirAbsorptionHF);
783 mPropsDirty = true;
786 void ALCcontext::eax_set_hf_reference()
788 // TODO
791 void ALCcontext::eax_set_macro_fx_factor()
793 // TODO
796 void ALCcontext::eax_set_context()
798 eax_set_primary_fx_slot_id();
799 eax_set_distance_factor();
800 eax_set_air_absorbtion_hf();
801 eax_set_hf_reference();
804 void ALCcontext::eax_initialize_fx_slots()
806 eax_fx_slots_.initialize(*this);
807 eax_previous_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
808 eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
811 void ALCcontext::eax_initialize_sources()
813 std::unique_lock<std::mutex> source_lock{mSourceLock};
814 auto init_source = [this](ALsource &source) noexcept
815 { source.eax_initialize(this); };
816 ForEachSource(this, init_source);
819 void ALCcontext::eax_update_sources()
821 std::unique_lock<std::mutex> source_lock{mSourceLock};
822 auto update_source = [this](ALsource &source)
823 { source.eax_update(eax_context_shared_dirty_flags_); };
824 ForEachSource(this, update_source);
827 void ALCcontext::eax_validate_primary_fx_slot_id(
828 const GUID& primary_fx_slot_id)
830 if (primary_fx_slot_id != EAX_NULL_GUID &&
831 primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot0 &&
832 primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot0 &&
833 primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot1 &&
834 primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot1 &&
835 primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot2 &&
836 primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot2 &&
837 primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot3 &&
838 primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot3)
840 eax_fail("Unsupported primary FX slot id.");
844 void ALCcontext::eax_validate_distance_factor(
845 float distance_factor)
847 eax_validate_range<ContextException>(
848 "Distance Factor",
849 distance_factor,
850 EAXCONTEXT_MINDISTANCEFACTOR,
851 EAXCONTEXT_MAXDISTANCEFACTOR);
854 void ALCcontext::eax_validate_air_absorption_hf(
855 float air_absorption_hf)
857 eax_validate_range<ContextException>(
858 "Air Absorption HF",
859 air_absorption_hf,
860 EAXCONTEXT_MINAIRABSORPTIONHF,
861 EAXCONTEXT_MAXAIRABSORPTIONHF);
864 void ALCcontext::eax_validate_hf_reference(
865 float hf_reference)
867 eax_validate_range<ContextException>(
868 "HF Reference",
869 hf_reference,
870 EAXCONTEXT_MINHFREFERENCE,
871 EAXCONTEXT_MAXHFREFERENCE);
874 void ALCcontext::eax_validate_speaker_config(
875 unsigned long speaker_config)
877 switch (speaker_config)
879 case HEADPHONES:
880 case SPEAKERS_2:
881 case SPEAKERS_4:
882 case SPEAKERS_5:
883 case SPEAKERS_6:
884 case SPEAKERS_7:
885 break;
887 default:
888 eax_fail("Unsupported speaker configuration.");
892 void ALCcontext::eax_validate_session_eax_version(
893 unsigned long eax_version)
895 switch (eax_version)
897 case EAX_40:
898 case EAX_50:
899 break;
901 default:
902 eax_fail("Unsupported session EAX version.");
906 void ALCcontext::eax_validate_session_max_active_sends(
907 unsigned long max_active_sends)
909 eax_validate_range<ContextException>(
910 "Max Active Sends",
911 max_active_sends,
912 EAXCONTEXT_MINMAXACTIVESENDS,
913 EAXCONTEXT_MAXMAXACTIVESENDS);
916 void ALCcontext::eax_validate_session(
917 const EAXSESSIONPROPERTIES& eax_session)
919 eax_validate_session_eax_version(eax_session.ulEAXVersion);
920 eax_validate_session_max_active_sends(eax_session.ulMaxActiveSends);
923 void ALCcontext::eax_validate_macro_fx_factor(
924 float macro_fx_factor)
926 eax_validate_range<ContextException>(
927 "Macro FX Factor",
928 macro_fx_factor,
929 EAXCONTEXT_MINMACROFXFACTOR,
930 EAXCONTEXT_MAXMACROFXFACTOR);
933 void ALCcontext::eax_validate_context_all(
934 const EAX40CONTEXTPROPERTIES& context_all)
936 eax_validate_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
937 eax_validate_distance_factor(context_all.flDistanceFactor);
938 eax_validate_air_absorption_hf(context_all.flAirAbsorptionHF);
939 eax_validate_hf_reference(context_all.flHFReference);
942 void ALCcontext::eax_validate_context_all(
943 const EAX50CONTEXTPROPERTIES& context_all)
945 eax_validate_context_all(static_cast<const EAX40CONTEXTPROPERTIES>(context_all));
946 eax_validate_macro_fx_factor(context_all.flMacroFXFactor);
949 void ALCcontext::eax_defer_primary_fx_slot_id(
950 const GUID& primary_fx_slot_id)
952 eax_d_.context.guidPrimaryFXSlotID = primary_fx_slot_id;
954 eax_context_dirty_flags_.guidPrimaryFXSlotID =
955 (eax_.context.guidPrimaryFXSlotID != eax_d_.context.guidPrimaryFXSlotID);
958 void ALCcontext::eax_defer_distance_factor(
959 float distance_factor)
961 eax_d_.context.flDistanceFactor = distance_factor;
963 eax_context_dirty_flags_.flDistanceFactor =
964 (eax_.context.flDistanceFactor != eax_d_.context.flDistanceFactor);
967 void ALCcontext::eax_defer_air_absorption_hf(
968 float air_absorption_hf)
970 eax_d_.context.flAirAbsorptionHF = air_absorption_hf;
972 eax_context_dirty_flags_.flAirAbsorptionHF =
973 (eax_.context.flAirAbsorptionHF != eax_d_.context.flAirAbsorptionHF);
976 void ALCcontext::eax_defer_hf_reference(
977 float hf_reference)
979 eax_d_.context.flHFReference = hf_reference;
981 eax_context_dirty_flags_.flHFReference =
982 (eax_.context.flHFReference != eax_d_.context.flHFReference);
985 void ALCcontext::eax_defer_macro_fx_factor(
986 float macro_fx_factor)
988 eax_d_.context.flMacroFXFactor = macro_fx_factor;
990 eax_context_dirty_flags_.flMacroFXFactor =
991 (eax_.context.flMacroFXFactor != eax_d_.context.flMacroFXFactor);
994 void ALCcontext::eax_defer_context_all(
995 const EAX40CONTEXTPROPERTIES& context_all)
997 eax_defer_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
998 eax_defer_distance_factor(context_all.flDistanceFactor);
999 eax_defer_air_absorption_hf(context_all.flAirAbsorptionHF);
1000 eax_defer_hf_reference(context_all.flHFReference);
1003 void ALCcontext::eax_defer_context_all(
1004 const EAX50CONTEXTPROPERTIES& context_all)
1006 eax_defer_context_all(static_cast<const EAX40CONTEXTPROPERTIES&>(context_all));
1007 eax_defer_macro_fx_factor(context_all.flMacroFXFactor);
1010 void ALCcontext::eax_defer_context_all(
1011 const EaxEaxCall& eax_call)
1013 switch(eax_call.get_version())
1015 case 4:
1017 const auto& context_all =
1018 eax_call.get_value<ContextException, EAX40CONTEXTPROPERTIES>();
1020 eax_validate_context_all(context_all);
1021 eax_defer_context_all(context_all);
1023 break;
1025 case 5:
1027 const auto& context_all =
1028 eax_call.get_value<ContextException, EAX50CONTEXTPROPERTIES>();
1030 eax_validate_context_all(context_all);
1031 eax_defer_context_all(context_all);
1033 break;
1035 default:
1036 eax_fail("Unsupported EAX version.");
1040 void ALCcontext::eax_defer_primary_fx_slot_id(
1041 const EaxEaxCall& eax_call)
1043 const auto& primary_fx_slot_id =
1044 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID)>();
1046 eax_validate_primary_fx_slot_id(primary_fx_slot_id);
1047 eax_defer_primary_fx_slot_id(primary_fx_slot_id);
1050 void ALCcontext::eax_defer_distance_factor(
1051 const EaxEaxCall& eax_call)
1053 const auto& distance_factor =
1054 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flDistanceFactor)>();
1056 eax_validate_distance_factor(distance_factor);
1057 eax_defer_distance_factor(distance_factor);
1060 void ALCcontext::eax_defer_air_absorption_hf(
1061 const EaxEaxCall& eax_call)
1063 const auto& air_absorption_hf =
1064 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flAirAbsorptionHF)>();
1066 eax_validate_air_absorption_hf(air_absorption_hf);
1067 eax_defer_air_absorption_hf(air_absorption_hf);
1070 void ALCcontext::eax_defer_hf_reference(
1071 const EaxEaxCall& eax_call)
1073 const auto& hf_reference =
1074 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flHFReference)>();
1076 eax_validate_hf_reference(hf_reference);
1077 eax_defer_hf_reference(hf_reference);
1080 void ALCcontext::eax_set_session(
1081 const EaxEaxCall& eax_call)
1083 const auto& eax_session =
1084 eax_call.get_value<ContextException, const EAXSESSIONPROPERTIES>();
1086 eax_validate_session(eax_session);
1088 eax_session_ = eax_session;
1091 void ALCcontext::eax_defer_macro_fx_factor(
1092 const EaxEaxCall& eax_call)
1094 const auto& macro_fx_factor =
1095 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flMacroFXFactor)>();
1097 eax_validate_macro_fx_factor(macro_fx_factor);
1098 eax_defer_macro_fx_factor(macro_fx_factor);
1101 void ALCcontext::eax_set(
1102 const EaxEaxCall& eax_call)
1104 switch (eax_call.get_property_id())
1106 case EAXCONTEXT_NONE:
1107 break;
1109 case EAXCONTEXT_ALLPARAMETERS:
1110 eax_defer_context_all(eax_call);
1111 break;
1113 case EAXCONTEXT_PRIMARYFXSLOTID:
1114 eax_defer_primary_fx_slot_id(eax_call);
1115 break;
1117 case EAXCONTEXT_DISTANCEFACTOR:
1118 eax_defer_distance_factor(eax_call);
1119 break;
1121 case EAXCONTEXT_AIRABSORPTIONHF:
1122 eax_defer_air_absorption_hf(eax_call);
1123 break;
1125 case EAXCONTEXT_HFREFERENCE:
1126 eax_defer_hf_reference(eax_call);
1127 break;
1129 case EAXCONTEXT_LASTERROR:
1130 eax_fail("Last error is read-only.");
1132 case EAXCONTEXT_SPEAKERCONFIG:
1133 eax_fail("Speaker configuration is read-only.");
1135 case EAXCONTEXT_EAXSESSION:
1136 eax_set_session(eax_call);
1137 break;
1139 case EAXCONTEXT_MACROFXFACTOR:
1140 eax_defer_macro_fx_factor(eax_call);
1141 break;
1143 default:
1144 eax_fail("Unsupported property id.");
1148 void ALCcontext::eax_apply_deferred()
1150 if (eax_context_dirty_flags_ == ContextDirtyFlags{})
1152 return;
1155 eax_ = eax_d_;
1157 if (eax_context_dirty_flags_.guidPrimaryFXSlotID)
1159 eax_context_shared_dirty_flags_.primary_fx_slot_id = true;
1160 eax_set_primary_fx_slot_id();
1163 if (eax_context_dirty_flags_.flDistanceFactor)
1165 eax_set_distance_factor();
1168 if (eax_context_dirty_flags_.flAirAbsorptionHF)
1170 eax_set_air_absorbtion_hf();
1173 if (eax_context_dirty_flags_.flHFReference)
1175 eax_set_hf_reference();
1178 if (eax_context_dirty_flags_.flMacroFXFactor)
1180 eax_set_macro_fx_factor();
1183 if (eax_context_shared_dirty_flags_ != EaxContextSharedDirtyFlags{})
1185 eax_update_sources();
1188 eax_context_shared_dirty_flags_ = EaxContextSharedDirtyFlags{};
1189 eax_context_dirty_flags_ = ContextDirtyFlags{};
1193 namespace
1197 class EaxSetException :
1198 public EaxException
1200 public:
1201 explicit EaxSetException(
1202 const char* message)
1204 EaxException{"EAX_SET", message}
1207 }; // EaxSetException
1210 [[noreturn]]
1211 void eax_fail_set(
1212 const char* message)
1214 throw EaxSetException{message};
1218 class EaxGetException :
1219 public EaxException
1221 public:
1222 explicit EaxGetException(
1223 const char* message)
1225 EaxException{"EAX_GET", message}
1228 }; // EaxGetException
1231 [[noreturn]]
1232 void eax_fail_get(
1233 const char* message)
1235 throw EaxGetException{message};
1239 } // namespace
1242 FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
1243 const GUID* property_set_id,
1244 ALuint property_id,
1245 ALuint property_source_id,
1246 ALvoid* property_value,
1247 ALuint property_value_size) noexcept
1250 auto context = GetContextRef();
1252 if (!context)
1254 eax_fail_set("No current context.");
1257 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1259 return context->eax_eax_set(
1260 property_set_id,
1261 property_id,
1262 property_source_id,
1263 property_value,
1264 property_value_size
1267 catch (...)
1269 eax_log_exception(__func__);
1270 return AL_INVALID_OPERATION;
1273 FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
1274 const GUID* property_set_id,
1275 ALuint property_id,
1276 ALuint property_source_id,
1277 ALvoid* property_value,
1278 ALuint property_value_size) noexcept
1281 auto context = GetContextRef();
1283 if (!context)
1285 eax_fail_get("No current context.");
1288 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1290 return context->eax_eax_get(
1291 property_set_id,
1292 property_id,
1293 property_source_id,
1294 property_value,
1295 property_value_size
1298 catch (...)
1300 eax_log_exception(__func__);
1301 return AL_INVALID_OPERATION;
1303 #endif // ALSOFT_EAX