Finalize AL_SOFT_UHJ
[openal-soft.git] / alc / context.cpp
blob456c054eaec4ea46e714fa1b796aa31589102936
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 /* This could also be HEADPHONES, since headphones-based HRTF and Ambi3D
562 * provide full-sphere surround sound. Depends if apps are more likely to
563 * consider headphones or 7.1 for surround sound support.
565 case DevFmtAmbi3D: return SPEAKERS_7;
567 ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
568 return HEADPHONES;
570 #undef EAX_PREFIX
573 void ALCcontext::eax_update_speaker_configuration()
575 eax_speaker_config_ = eax_detect_speaker_configuration();
578 void ALCcontext::eax_set_last_error_defaults() noexcept
580 eax_last_error_ = EAX_OK;
583 void ALCcontext::eax_set_session_defaults() noexcept
585 eax_session_.ulEAXVersion = EAXCONTEXT_MINEAXSESSION;
586 eax_session_.ulMaxActiveSends = EAXCONTEXT_DEFAULTMAXACTIVESENDS;
589 void ALCcontext::eax_set_context_defaults() noexcept
591 eax_.context.guidPrimaryFXSlotID = EAXCONTEXT_DEFAULTPRIMARYFXSLOTID;
592 eax_.context.flDistanceFactor = EAXCONTEXT_DEFAULTDISTANCEFACTOR;
593 eax_.context.flAirAbsorptionHF = EAXCONTEXT_DEFAULTAIRABSORPTIONHF;
594 eax_.context.flHFReference = EAXCONTEXT_DEFAULTHFREFERENCE;
597 void ALCcontext::eax_set_defaults() noexcept
599 eax_set_last_error_defaults();
600 eax_set_session_defaults();
601 eax_set_context_defaults();
603 eax_d_ = eax_;
606 void ALCcontext::eax_unlock_legacy_fx_slots(const EaxEaxCall& eax_call) noexcept
608 if (eax_call.get_version() != 5 || eax_are_legacy_fx_slots_unlocked_)
609 return;
611 eax_are_legacy_fx_slots_unlocked_ = true;
612 eax_fx_slots_.unlock_legacy();
615 void ALCcontext::eax_dispatch_fx_slot(
616 const EaxEaxCall& eax_call)
618 const auto fx_slot_index = eax_call.get_fx_slot_index();
619 if(!fx_slot_index.has_value())
620 eax_fail("Invalid fx slot index.");
622 auto& fx_slot = eax_get_fx_slot(*fx_slot_index);
623 if(fx_slot.eax_dispatch(eax_call))
625 std::lock_guard<std::mutex> source_lock{mSourceLock};
626 eax_update_filters();
630 void ALCcontext::eax_dispatch_source(
631 const EaxEaxCall& eax_call)
633 const auto source_id = eax_call.get_property_al_name();
635 std::lock_guard<std::mutex> source_lock{mSourceLock};
637 const auto source = ALsource::eax_lookup_source(*this, source_id);
639 if (!source)
641 eax_fail("Source not found.");
644 source->eax_dispatch(eax_call);
647 void ALCcontext::eax_get_primary_fx_slot_id(
648 const EaxEaxCall& eax_call)
650 eax_call.set_value<ContextException>(eax_.context.guidPrimaryFXSlotID);
653 void ALCcontext::eax_get_distance_factor(
654 const EaxEaxCall& eax_call)
656 eax_call.set_value<ContextException>(eax_.context.flDistanceFactor);
659 void ALCcontext::eax_get_air_absorption_hf(
660 const EaxEaxCall& eax_call)
662 eax_call.set_value<ContextException>(eax_.context.flAirAbsorptionHF);
665 void ALCcontext::eax_get_hf_reference(
666 const EaxEaxCall& eax_call)
668 eax_call.set_value<ContextException>(eax_.context.flHFReference);
671 void ALCcontext::eax_get_last_error(
672 const EaxEaxCall& eax_call)
674 const auto eax_last_error = eax_last_error_;
675 eax_last_error_ = EAX_OK;
676 eax_call.set_value<ContextException>(eax_last_error);
679 void ALCcontext::eax_get_speaker_config(
680 const EaxEaxCall& eax_call)
682 eax_call.set_value<ContextException>(eax_speaker_config_);
685 void ALCcontext::eax_get_session(
686 const EaxEaxCall& eax_call)
688 eax_call.set_value<ContextException>(eax_session_);
691 void ALCcontext::eax_get_macro_fx_factor(
692 const EaxEaxCall& eax_call)
694 eax_call.set_value<ContextException>(eax_.context.flMacroFXFactor);
697 void ALCcontext::eax_get_context_all(
698 const EaxEaxCall& eax_call)
700 switch (eax_call.get_version())
702 case 4:
703 eax_call.set_value<ContextException>(static_cast<const EAX40CONTEXTPROPERTIES&>(eax_.context));
704 break;
706 case 5:
707 eax_call.set_value<ContextException>(static_cast<const EAX50CONTEXTPROPERTIES&>(eax_.context));
708 break;
710 default:
711 eax_fail("Unsupported EAX version.");
715 void ALCcontext::eax_get(
716 const EaxEaxCall& eax_call)
718 switch (eax_call.get_property_id())
720 case EAXCONTEXT_NONE:
721 break;
723 case EAXCONTEXT_ALLPARAMETERS:
724 eax_get_context_all(eax_call);
725 break;
727 case EAXCONTEXT_PRIMARYFXSLOTID:
728 eax_get_primary_fx_slot_id(eax_call);
729 break;
731 case EAXCONTEXT_DISTANCEFACTOR:
732 eax_get_distance_factor(eax_call);
733 break;
735 case EAXCONTEXT_AIRABSORPTIONHF:
736 eax_get_air_absorption_hf(eax_call);
737 break;
739 case EAXCONTEXT_HFREFERENCE:
740 eax_get_hf_reference(eax_call);
741 break;
743 case EAXCONTEXT_LASTERROR:
744 eax_get_last_error(eax_call);
745 break;
747 case EAXCONTEXT_SPEAKERCONFIG:
748 eax_get_speaker_config(eax_call);
749 break;
751 case EAXCONTEXT_EAXSESSION:
752 eax_get_session(eax_call);
753 break;
755 case EAXCONTEXT_MACROFXFACTOR:
756 eax_get_macro_fx_factor(eax_call);
757 break;
759 default:
760 eax_fail("Unsupported property id.");
764 void ALCcontext::eax_set_primary_fx_slot_id()
766 eax_previous_primary_fx_slot_index_ = eax_primary_fx_slot_index_;
767 eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
770 void ALCcontext::eax_set_distance_factor()
772 mListener.mMetersPerUnit = eax_.context.flDistanceFactor;
773 mPropsDirty = true;
776 void ALCcontext::eax_set_air_absorbtion_hf()
778 mAirAbsorptionGainHF = level_mb_to_gain(eax_.context.flAirAbsorptionHF);
779 mPropsDirty = true;
782 void ALCcontext::eax_set_hf_reference()
784 // TODO
787 void ALCcontext::eax_set_macro_fx_factor()
789 // TODO
792 void ALCcontext::eax_set_context()
794 eax_set_primary_fx_slot_id();
795 eax_set_distance_factor();
796 eax_set_air_absorbtion_hf();
797 eax_set_hf_reference();
800 void ALCcontext::eax_initialize_fx_slots()
802 eax_fx_slots_.initialize(*this);
803 eax_previous_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
804 eax_primary_fx_slot_index_ = eax_.context.guidPrimaryFXSlotID;
807 void ALCcontext::eax_initialize_sources()
809 std::unique_lock<std::mutex> source_lock{mSourceLock};
810 auto init_source = [this](ALsource &source) noexcept
811 { source.eax_initialize(this); };
812 ForEachSource(this, init_source);
815 void ALCcontext::eax_update_sources()
817 std::unique_lock<std::mutex> source_lock{mSourceLock};
818 auto update_source = [this](ALsource &source)
819 { source.eax_update(eax_context_shared_dirty_flags_); };
820 ForEachSource(this, update_source);
823 void ALCcontext::eax_validate_primary_fx_slot_id(
824 const GUID& primary_fx_slot_id)
826 if (primary_fx_slot_id != EAX_NULL_GUID &&
827 primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot0 &&
828 primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot0 &&
829 primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot1 &&
830 primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot1 &&
831 primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot2 &&
832 primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot2 &&
833 primary_fx_slot_id != EAXPROPERTYID_EAX40_FXSlot3 &&
834 primary_fx_slot_id != EAXPROPERTYID_EAX50_FXSlot3)
836 eax_fail("Unsupported primary FX slot id.");
840 void ALCcontext::eax_validate_distance_factor(
841 float distance_factor)
843 eax_validate_range<ContextException>(
844 "Distance Factor",
845 distance_factor,
846 EAXCONTEXT_MINDISTANCEFACTOR,
847 EAXCONTEXT_MAXDISTANCEFACTOR);
850 void ALCcontext::eax_validate_air_absorption_hf(
851 float air_absorption_hf)
853 eax_validate_range<ContextException>(
854 "Air Absorption HF",
855 air_absorption_hf,
856 EAXCONTEXT_MINAIRABSORPTIONHF,
857 EAXCONTEXT_MAXAIRABSORPTIONHF);
860 void ALCcontext::eax_validate_hf_reference(
861 float hf_reference)
863 eax_validate_range<ContextException>(
864 "HF Reference",
865 hf_reference,
866 EAXCONTEXT_MINHFREFERENCE,
867 EAXCONTEXT_MAXHFREFERENCE);
870 void ALCcontext::eax_validate_speaker_config(
871 unsigned long speaker_config)
873 switch (speaker_config)
875 case HEADPHONES:
876 case SPEAKERS_2:
877 case SPEAKERS_4:
878 case SPEAKERS_5:
879 case SPEAKERS_6:
880 case SPEAKERS_7:
881 break;
883 default:
884 eax_fail("Unsupported speaker configuration.");
888 void ALCcontext::eax_validate_session_eax_version(
889 unsigned long eax_version)
891 switch (eax_version)
893 case EAX_40:
894 case EAX_50:
895 break;
897 default:
898 eax_fail("Unsupported session EAX version.");
902 void ALCcontext::eax_validate_session_max_active_sends(
903 unsigned long max_active_sends)
905 eax_validate_range<ContextException>(
906 "Max Active Sends",
907 max_active_sends,
908 EAXCONTEXT_MINMAXACTIVESENDS,
909 EAXCONTEXT_MAXMAXACTIVESENDS);
912 void ALCcontext::eax_validate_session(
913 const EAXSESSIONPROPERTIES& eax_session)
915 eax_validate_session_eax_version(eax_session.ulEAXVersion);
916 eax_validate_session_max_active_sends(eax_session.ulMaxActiveSends);
919 void ALCcontext::eax_validate_macro_fx_factor(
920 float macro_fx_factor)
922 eax_validate_range<ContextException>(
923 "Macro FX Factor",
924 macro_fx_factor,
925 EAXCONTEXT_MINMACROFXFACTOR,
926 EAXCONTEXT_MAXMACROFXFACTOR);
929 void ALCcontext::eax_validate_context_all(
930 const EAX40CONTEXTPROPERTIES& context_all)
932 eax_validate_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
933 eax_validate_distance_factor(context_all.flDistanceFactor);
934 eax_validate_air_absorption_hf(context_all.flAirAbsorptionHF);
935 eax_validate_hf_reference(context_all.flHFReference);
938 void ALCcontext::eax_validate_context_all(
939 const EAX50CONTEXTPROPERTIES& context_all)
941 eax_validate_context_all(static_cast<const EAX40CONTEXTPROPERTIES>(context_all));
942 eax_validate_macro_fx_factor(context_all.flMacroFXFactor);
945 void ALCcontext::eax_defer_primary_fx_slot_id(
946 const GUID& primary_fx_slot_id)
948 eax_d_.context.guidPrimaryFXSlotID = primary_fx_slot_id;
950 eax_context_dirty_flags_.guidPrimaryFXSlotID =
951 (eax_.context.guidPrimaryFXSlotID != eax_d_.context.guidPrimaryFXSlotID);
954 void ALCcontext::eax_defer_distance_factor(
955 float distance_factor)
957 eax_d_.context.flDistanceFactor = distance_factor;
959 eax_context_dirty_flags_.flDistanceFactor =
960 (eax_.context.flDistanceFactor != eax_d_.context.flDistanceFactor);
963 void ALCcontext::eax_defer_air_absorption_hf(
964 float air_absorption_hf)
966 eax_d_.context.flAirAbsorptionHF = air_absorption_hf;
968 eax_context_dirty_flags_.flAirAbsorptionHF =
969 (eax_.context.flAirAbsorptionHF != eax_d_.context.flAirAbsorptionHF);
972 void ALCcontext::eax_defer_hf_reference(
973 float hf_reference)
975 eax_d_.context.flHFReference = hf_reference;
977 eax_context_dirty_flags_.flHFReference =
978 (eax_.context.flHFReference != eax_d_.context.flHFReference);
981 void ALCcontext::eax_defer_macro_fx_factor(
982 float macro_fx_factor)
984 eax_d_.context.flMacroFXFactor = macro_fx_factor;
986 eax_context_dirty_flags_.flMacroFXFactor =
987 (eax_.context.flMacroFXFactor != eax_d_.context.flMacroFXFactor);
990 void ALCcontext::eax_defer_context_all(
991 const EAX40CONTEXTPROPERTIES& context_all)
993 eax_defer_primary_fx_slot_id(context_all.guidPrimaryFXSlotID);
994 eax_defer_distance_factor(context_all.flDistanceFactor);
995 eax_defer_air_absorption_hf(context_all.flAirAbsorptionHF);
996 eax_defer_hf_reference(context_all.flHFReference);
999 void ALCcontext::eax_defer_context_all(
1000 const EAX50CONTEXTPROPERTIES& context_all)
1002 eax_defer_context_all(static_cast<const EAX40CONTEXTPROPERTIES&>(context_all));
1003 eax_defer_macro_fx_factor(context_all.flMacroFXFactor);
1006 void ALCcontext::eax_defer_context_all(
1007 const EaxEaxCall& eax_call)
1009 switch(eax_call.get_version())
1011 case 4:
1013 const auto& context_all =
1014 eax_call.get_value<ContextException, EAX40CONTEXTPROPERTIES>();
1016 eax_validate_context_all(context_all);
1017 eax_defer_context_all(context_all);
1019 break;
1021 case 5:
1023 const auto& context_all =
1024 eax_call.get_value<ContextException, EAX50CONTEXTPROPERTIES>();
1026 eax_validate_context_all(context_all);
1027 eax_defer_context_all(context_all);
1029 break;
1031 default:
1032 eax_fail("Unsupported EAX version.");
1036 void ALCcontext::eax_defer_primary_fx_slot_id(
1037 const EaxEaxCall& eax_call)
1039 const auto& primary_fx_slot_id =
1040 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID)>();
1042 eax_validate_primary_fx_slot_id(primary_fx_slot_id);
1043 eax_defer_primary_fx_slot_id(primary_fx_slot_id);
1046 void ALCcontext::eax_defer_distance_factor(
1047 const EaxEaxCall& eax_call)
1049 const auto& distance_factor =
1050 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flDistanceFactor)>();
1052 eax_validate_distance_factor(distance_factor);
1053 eax_defer_distance_factor(distance_factor);
1056 void ALCcontext::eax_defer_air_absorption_hf(
1057 const EaxEaxCall& eax_call)
1059 const auto& air_absorption_hf =
1060 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flAirAbsorptionHF)>();
1062 eax_validate_air_absorption_hf(air_absorption_hf);
1063 eax_defer_air_absorption_hf(air_absorption_hf);
1066 void ALCcontext::eax_defer_hf_reference(
1067 const EaxEaxCall& eax_call)
1069 const auto& hf_reference =
1070 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flHFReference)>();
1072 eax_validate_hf_reference(hf_reference);
1073 eax_defer_hf_reference(hf_reference);
1076 void ALCcontext::eax_set_session(
1077 const EaxEaxCall& eax_call)
1079 const auto& eax_session =
1080 eax_call.get_value<ContextException, const EAXSESSIONPROPERTIES>();
1082 eax_validate_session(eax_session);
1084 eax_session_ = eax_session;
1087 void ALCcontext::eax_defer_macro_fx_factor(
1088 const EaxEaxCall& eax_call)
1090 const auto& macro_fx_factor =
1091 eax_call.get_value<ContextException, const decltype(EAX50CONTEXTPROPERTIES::flMacroFXFactor)>();
1093 eax_validate_macro_fx_factor(macro_fx_factor);
1094 eax_defer_macro_fx_factor(macro_fx_factor);
1097 void ALCcontext::eax_set(
1098 const EaxEaxCall& eax_call)
1100 switch (eax_call.get_property_id())
1102 case EAXCONTEXT_NONE:
1103 break;
1105 case EAXCONTEXT_ALLPARAMETERS:
1106 eax_defer_context_all(eax_call);
1107 break;
1109 case EAXCONTEXT_PRIMARYFXSLOTID:
1110 eax_defer_primary_fx_slot_id(eax_call);
1111 break;
1113 case EAXCONTEXT_DISTANCEFACTOR:
1114 eax_defer_distance_factor(eax_call);
1115 break;
1117 case EAXCONTEXT_AIRABSORPTIONHF:
1118 eax_defer_air_absorption_hf(eax_call);
1119 break;
1121 case EAXCONTEXT_HFREFERENCE:
1122 eax_defer_hf_reference(eax_call);
1123 break;
1125 case EAXCONTEXT_LASTERROR:
1126 eax_fail("Last error is read-only.");
1128 case EAXCONTEXT_SPEAKERCONFIG:
1129 eax_fail("Speaker configuration is read-only.");
1131 case EAXCONTEXT_EAXSESSION:
1132 eax_set_session(eax_call);
1133 break;
1135 case EAXCONTEXT_MACROFXFACTOR:
1136 eax_defer_macro_fx_factor(eax_call);
1137 break;
1139 default:
1140 eax_fail("Unsupported property id.");
1144 void ALCcontext::eax_apply_deferred()
1146 if (eax_context_dirty_flags_ == ContextDirtyFlags{})
1148 return;
1151 eax_ = eax_d_;
1153 if (eax_context_dirty_flags_.guidPrimaryFXSlotID)
1155 eax_context_shared_dirty_flags_.primary_fx_slot_id = true;
1156 eax_set_primary_fx_slot_id();
1159 if (eax_context_dirty_flags_.flDistanceFactor)
1161 eax_set_distance_factor();
1164 if (eax_context_dirty_flags_.flAirAbsorptionHF)
1166 eax_set_air_absorbtion_hf();
1169 if (eax_context_dirty_flags_.flHFReference)
1171 eax_set_hf_reference();
1174 if (eax_context_dirty_flags_.flMacroFXFactor)
1176 eax_set_macro_fx_factor();
1179 if (eax_context_shared_dirty_flags_ != EaxContextSharedDirtyFlags{})
1181 eax_update_sources();
1184 eax_context_shared_dirty_flags_ = EaxContextSharedDirtyFlags{};
1185 eax_context_dirty_flags_ = ContextDirtyFlags{};
1189 namespace
1193 class EaxSetException :
1194 public EaxException
1196 public:
1197 explicit EaxSetException(
1198 const char* message)
1200 EaxException{"EAX_SET", message}
1203 }; // EaxSetException
1206 [[noreturn]]
1207 void eax_fail_set(
1208 const char* message)
1210 throw EaxSetException{message};
1214 class EaxGetException :
1215 public EaxException
1217 public:
1218 explicit EaxGetException(
1219 const char* message)
1221 EaxException{"EAX_GET", message}
1224 }; // EaxGetException
1227 [[noreturn]]
1228 void eax_fail_get(
1229 const char* message)
1231 throw EaxGetException{message};
1235 } // namespace
1238 FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
1239 const GUID* property_set_id,
1240 ALuint property_id,
1241 ALuint property_source_id,
1242 ALvoid* property_value,
1243 ALuint property_value_size) noexcept
1246 auto context = GetContextRef();
1248 if (!context)
1250 eax_fail_set("No current context.");
1253 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1255 return context->eax_eax_set(
1256 property_set_id,
1257 property_id,
1258 property_source_id,
1259 property_value,
1260 property_value_size
1263 catch (...)
1265 eax_log_exception(__func__);
1266 return AL_INVALID_OPERATION;
1269 FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
1270 const GUID* property_set_id,
1271 ALuint property_id,
1272 ALuint property_source_id,
1273 ALvoid* property_value,
1274 ALuint property_value_size) noexcept
1277 auto context = GetContextRef();
1279 if (!context)
1281 eax_fail_get("No current context.");
1284 std::lock_guard<std::mutex> prop_lock{context->mPropLock};
1286 return context->eax_eax_get(
1287 property_set_id,
1288 property_id,
1289 property_source_id,
1290 property_value,
1291 property_value_size
1294 catch (...)
1296 eax_log_exception(__func__);
1297 return AL_INVALID_OPERATION;
1299 #endif // ALSOFT_EAX