Don't call log10 with values too close to 0
[openal-soft.git] / al / effect.cpp
blob6509d755d93fb914e8c5d29a092ab27404ec99b9
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include "effect.h"
25 #include <algorithm>
26 #include <cstdint>
27 #include <cstring>
28 #include <iterator>
29 #include <memory>
30 #include <mutex>
31 #include <numeric>
32 #include <string>
33 #include <type_traits>
34 #include <unordered_map>
35 #include <utility>
36 #include <variant>
37 #include <vector>
39 #include "AL/al.h"
40 #include "AL/alc.h"
41 #include "AL/alext.h"
42 #include "AL/efx-presets.h"
43 #include "AL/efx.h"
45 #include "al/effects/effects.h"
46 #include "albit.h"
47 #include "alc/context.h"
48 #include "alc/device.h"
49 #include "alc/inprogext.h"
50 #include "almalloc.h"
51 #include "alnumeric.h"
52 #include "alspan.h"
53 #include "alstring.h"
54 #include "core/logging.h"
55 #include "direct_defs.h"
56 #include "error.h"
57 #include "intrusive_ptr.h"
58 #include "opthelpers.h"
61 const std::array<EffectList,16> gEffectList{{
62 { "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
63 { "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
64 { "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
65 { "chorus", CHORUS_EFFECT, AL_EFFECT_CHORUS },
66 { "compressor", COMPRESSOR_EFFECT, AL_EFFECT_COMPRESSOR },
67 { "distortion", DISTORTION_EFFECT, AL_EFFECT_DISTORTION },
68 { "echo", ECHO_EFFECT, AL_EFFECT_ECHO },
69 { "equalizer", EQUALIZER_EFFECT, AL_EFFECT_EQUALIZER },
70 { "flanger", FLANGER_EFFECT, AL_EFFECT_FLANGER },
71 { "fshifter", FSHIFTER_EFFECT, AL_EFFECT_FREQUENCY_SHIFTER },
72 { "modulator", MODULATOR_EFFECT, AL_EFFECT_RING_MODULATOR },
73 { "pshifter", PSHIFTER_EFFECT, AL_EFFECT_PITCH_SHIFTER },
74 { "vmorpher", VMORPHER_EFFECT, AL_EFFECT_VOCAL_MORPHER },
75 { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
76 { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE },
77 { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT },
78 }};
81 namespace {
83 using SubListAllocator = al::allocator<std::array<ALeffect,64>>;
85 constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps&
87 switch(type)
89 case AL_EFFECT_NULL: return NullEffectProps;
90 case AL_EFFECT_EAXREVERB: return ReverbEffectProps;
91 case AL_EFFECT_REVERB: return StdReverbEffectProps;
92 case AL_EFFECT_AUTOWAH: return AutowahEffectProps;
93 case AL_EFFECT_CHORUS: return ChorusEffectProps;
94 case AL_EFFECT_COMPRESSOR: return CompressorEffectProps;
95 case AL_EFFECT_DISTORTION: return DistortionEffectProps;
96 case AL_EFFECT_ECHO: return EchoEffectProps;
97 case AL_EFFECT_EQUALIZER: return EqualizerEffectProps;
98 case AL_EFFECT_FLANGER: return FlangerEffectProps;
99 case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps;
100 case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps;
101 case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps;
102 case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps;
103 case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps;
104 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps;
105 case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps;
107 return NullEffectProps;
110 void InitEffectParams(ALeffect *effect, ALenum type) noexcept
112 switch(type)
114 case AL_EFFECT_NULL: effect->PropsVariant.emplace<NullEffectHandler>(); break;
115 case AL_EFFECT_EAXREVERB: effect->PropsVariant.emplace<ReverbEffectHandler>(); break;
116 case AL_EFFECT_REVERB: effect->PropsVariant.emplace<StdReverbEffectHandler>(); break;
117 case AL_EFFECT_AUTOWAH: effect->PropsVariant.emplace<AutowahEffectHandler>(); break;
118 case AL_EFFECT_CHORUS: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
119 case AL_EFFECT_COMPRESSOR: effect->PropsVariant.emplace<CompressorEffectHandler>(); break;
120 case AL_EFFECT_DISTORTION: effect->PropsVariant.emplace<DistortionEffectHandler>(); break;
121 case AL_EFFECT_ECHO: effect->PropsVariant.emplace<EchoEffectHandler>(); break;
122 case AL_EFFECT_EQUALIZER: effect->PropsVariant.emplace<EqualizerEffectHandler>(); break;
123 case AL_EFFECT_FLANGER: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
124 case AL_EFFECT_FREQUENCY_SHIFTER: effect->PropsVariant.emplace<FshifterEffectHandler>(); break;
125 case AL_EFFECT_RING_MODULATOR: effect->PropsVariant.emplace<ModulatorEffectHandler>(); break;
126 case AL_EFFECT_PITCH_SHIFTER: effect->PropsVariant.emplace<PshifterEffectHandler>(); break;
127 case AL_EFFECT_VOCAL_MORPHER: effect->PropsVariant.emplace<VmorpherEffectHandler>(); break;
128 case AL_EFFECT_DEDICATED_DIALOGUE:
129 effect->PropsVariant.emplace<DedicatedDialogEffectHandler>();
130 break;
131 case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
132 effect->PropsVariant.emplace<DedicatedLfeEffectHandler>();
133 break;
134 case AL_EFFECT_CONVOLUTION_SOFT:
135 effect->PropsVariant.emplace<ConvolutionEffectHandler>();
136 break;
138 effect->Props = GetDefaultProps(type);
139 effect->type = type;
142 auto EnsureEffects(ALCdevice *device, size_t needed) noexcept -> bool
143 try {
144 size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
145 [](size_t cur, const EffectSubList &sublist) noexcept -> size_t
146 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
148 while(needed > count)
150 if(device->EffectList.size() >= 1<<25) UNLIKELY
151 return false;
153 EffectSubList sublist{};
154 sublist.FreeMask = ~0_u64;
155 sublist.Effects = SubListAllocator{}.allocate(1);
156 device->EffectList.emplace_back(std::move(sublist));
157 count += std::tuple_size_v<SubListAllocator::value_type>;
159 return true;
161 catch(...) {
162 return false;
165 ALeffect *AllocEffect(ALCdevice *device) noexcept
167 auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
168 [](const EffectSubList &entry) noexcept -> bool
169 { return entry.FreeMask != 0; });
170 auto lidx = static_cast<ALuint>(std::distance(device->EffectList.begin(), sublist));
171 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
172 ASSUME(slidx < 64);
174 ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))};
175 InitEffectParams(effect, AL_EFFECT_NULL);
177 /* Add 1 to avoid effect ID 0. */
178 effect->id = ((lidx<<6) | slidx) + 1;
180 sublist->FreeMask &= ~(1_u64 << slidx);
182 return effect;
185 void FreeEffect(ALCdevice *device, ALeffect *effect)
187 device->mEffectNames.erase(effect->id);
189 const ALuint id{effect->id - 1};
190 const size_t lidx{id >> 6};
191 const ALuint slidx{id & 0x3f};
193 std::destroy_at(effect);
195 device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
198 inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
200 const size_t lidx{(id-1) >> 6};
201 const ALuint slidx{(id-1) & 0x3f};
203 if(lidx >= device->EffectList.size()) UNLIKELY
204 return nullptr;
205 EffectSubList &sublist = device->EffectList[lidx];
206 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
207 return nullptr;
208 return al::to_address(sublist.Effects->begin() + slidx);
211 } // namespace
213 AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects)
214 FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
215 try {
216 if(n < 0)
217 throw al::context_error{AL_INVALID_VALUE, "Generating %d effects", n};
218 if(n <= 0) UNLIKELY return;
220 ALCdevice *device{context->mALDevice.get()};
221 std::lock_guard<std::mutex> effectlock{device->EffectLock};
223 const al::span eids{effects, static_cast<ALuint>(n)};
224 if(!EnsureEffects(device, eids.size()))
225 throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n,
226 (n == 1) ? "" : "s"};
228 std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
230 catch(al::context_error& e) {
231 context->setError(e.errorCode(), "%s", e.what());
234 AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects)
235 FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n,
236 const ALuint *effects) noexcept
237 try {
238 if(n < 0)
239 throw al::context_error{AL_INVALID_VALUE, "Deleting %d effects", n};
240 if(n <= 0) UNLIKELY return;
242 ALCdevice *device{context->mALDevice.get()};
243 std::lock_guard<std::mutex> effectlock{device->EffectLock};
245 /* First try to find any effects that are invalid. */
246 auto validate_effect = [device](const ALuint eid) -> bool
247 { return !eid || LookupEffect(device, eid) != nullptr; };
249 const al::span eids{effects, static_cast<ALuint>(n)};
250 auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect);
251 if(inveffect != eids.end())
252 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", *inveffect};
254 /* All good. Delete non-0 effect IDs. */
255 auto delete_effect = [device](ALuint eid) -> void
257 if(ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr})
258 FreeEffect(device, effect);
260 std::for_each(eids.begin(), eids.end(), delete_effect);
262 catch(al::context_error& e) {
263 context->setError(e.errorCode(), "%s", e.what());
266 AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
267 FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
269 ALCdevice *device{context->mALDevice.get()};
270 std::lock_guard<std::mutex> effectlock{device->EffectLock};
271 if(!effect || LookupEffect(device, effect))
272 return AL_TRUE;
273 return AL_FALSE;
276 AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value)
277 FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
278 ALint value) noexcept
279 try {
280 ALCdevice *device{context->mALDevice.get()};
281 std::lock_guard<std::mutex> effectlock{device->EffectLock};
283 ALeffect *aleffect{LookupEffect(device, effect)};
284 if(!aleffect)
285 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
287 switch(param)
289 case AL_EFFECT_TYPE:
290 if(value != AL_EFFECT_NULL)
292 auto check_effect = [value](const EffectList &item) -> bool
293 { return value == item.val && !DisabledEffects.test(item.type); };
294 if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect))
295 throw al::context_error{AL_INVALID_VALUE, "Effect type 0x%04x not supported",
296 value};
299 InitEffectParams(aleffect, value);
300 return;
303 /* Call the appropriate handler */
304 std::visit([aleffect,param,value](auto &arg)
306 using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
307 using PropType = typename Type::prop_type;
308 return arg.SetParami(std::get<PropType>(aleffect->Props), param, value);
309 }, aleffect->PropsVariant);
311 catch(al::context_error& e) {
312 context->setError(e.errorCode(), "%s", e.what());
315 AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values)
316 FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
317 const ALint *values) noexcept
318 try {
319 switch(param)
321 case AL_EFFECT_TYPE:
322 alEffectiDirect(context, effect, param, *values);
323 return;
326 ALCdevice *device{context->mALDevice.get()};
327 std::lock_guard<std::mutex> effectlock{device->EffectLock};
329 ALeffect *aleffect{LookupEffect(device, effect)};
330 if(!aleffect)
331 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
333 /* Call the appropriate handler */
334 std::visit([aleffect,param,values](auto &arg)
336 using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
337 using PropType = typename Type::prop_type;
338 return arg.SetParamiv(std::get<PropType>(aleffect->Props), param, values);
339 }, aleffect->PropsVariant);
341 catch(al::context_error& e) {
342 context->setError(e.errorCode(), "%s", e.what());
345 AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value)
346 FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
347 ALfloat value) noexcept
348 try {
349 ALCdevice *device{context->mALDevice.get()};
350 std::lock_guard<std::mutex> effectlock{device->EffectLock};
352 ALeffect *aleffect{LookupEffect(device, effect)};
353 if(!aleffect) UNLIKELY
354 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
356 /* Call the appropriate handler */
357 std::visit([aleffect,param,value](auto &arg)
359 using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
360 using PropType = typename Type::prop_type;
361 return arg.SetParamf(std::get<PropType>(aleffect->Props), param, value);
362 }, aleffect->PropsVariant);
364 catch(al::context_error& e) {
365 context->setError(e.errorCode(), "%s", e.what());
368 AL_API DECL_FUNC3(void, alEffectfv, ALuint,effect, ALenum,param, const ALfloat*,values)
369 FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
370 const ALfloat *values) noexcept
371 try {
372 ALCdevice *device{context->mALDevice.get()};
373 std::lock_guard<std::mutex> effectlock{device->EffectLock};
375 ALeffect *aleffect{LookupEffect(device, effect)};
376 if(!aleffect)
377 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
379 /* Call the appropriate handler */
380 std::visit([aleffect,param,values](auto &arg)
382 using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
383 using PropType = typename Type::prop_type;
384 return arg.SetParamfv(std::get<PropType>(aleffect->Props), param, values);
385 }, aleffect->PropsVariant);
387 catch(al::context_error& e) {
388 context->setError(e.errorCode(), "%s", e.what());
391 AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value)
392 FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
393 ALint *value) noexcept
394 try {
395 ALCdevice *device{context->mALDevice.get()};
396 std::lock_guard<std::mutex> effectlock{device->EffectLock};
398 const ALeffect *aleffect{LookupEffect(device, effect)};
399 if(!aleffect)
400 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
402 switch(param)
404 case AL_EFFECT_TYPE:
405 *value = aleffect->type;
406 return;
409 /* Call the appropriate handler */
410 std::visit([aleffect,param,value](auto &arg)
412 using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
413 using PropType = typename Type::prop_type;
414 return arg.GetParami(std::get<PropType>(aleffect->Props), param, value);
415 }, aleffect->PropsVariant);
417 catch(al::context_error& e) {
418 context->setError(e.errorCode(), "%s", e.what());
421 AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values)
422 FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
423 ALint *values) noexcept
424 try {
425 switch(param)
427 case AL_EFFECT_TYPE:
428 alGetEffectiDirect(context, effect, param, values);
429 return;
432 ALCdevice *device{context->mALDevice.get()};
433 std::lock_guard<std::mutex> effectlock{device->EffectLock};
435 const ALeffect *aleffect{LookupEffect(device, effect)};
436 if(!aleffect)
437 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
439 /* Call the appropriate handler */
440 std::visit([aleffect,param,values](auto &arg)
442 using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
443 using PropType = typename Type::prop_type;
444 return arg.GetParamiv(std::get<PropType>(aleffect->Props), param, values);
445 }, aleffect->PropsVariant);
447 catch(al::context_error& e) {
448 context->setError(e.errorCode(), "%s", e.what());
451 AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value)
452 FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
453 ALfloat *value) noexcept
454 try {
455 ALCdevice *device{context->mALDevice.get()};
456 std::lock_guard<std::mutex> effectlock{device->EffectLock};
458 const ALeffect *aleffect{LookupEffect(device, effect)};
459 if(!aleffect)
460 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
462 /* Call the appropriate handler */
463 std::visit([aleffect,param,value](auto &arg)
465 using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
466 using PropType = typename Type::prop_type;
467 return arg.GetParamf(std::get<PropType>(aleffect->Props), param, value);
468 }, aleffect->PropsVariant);
470 catch(al::context_error& e) {
471 context->setError(e.errorCode(), "%s", e.what());
474 AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values)
475 FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
476 ALfloat *values) noexcept
477 try {
478 ALCdevice *device{context->mALDevice.get()};
479 std::lock_guard<std::mutex> effectlock{device->EffectLock};
481 const ALeffect *aleffect{LookupEffect(device, effect)};
482 if(!aleffect)
483 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
485 /* Call the appropriate handler */
486 std::visit([aleffect,param,values](auto &arg)
488 using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
489 using PropType = typename Type::prop_type;
490 return arg.GetParamfv(std::get<PropType>(aleffect->Props), param, values);
491 }, aleffect->PropsVariant);
493 catch(al::context_error& e) {
494 context->setError(e.errorCode(), "%s", e.what());
498 void InitEffect(ALeffect *effect)
500 InitEffectParams(effect, AL_EFFECT_NULL);
503 void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name)
505 ALCdevice *device{context->mALDevice.get()};
506 std::lock_guard<std::mutex> effectlock{device->EffectLock};
508 auto effect = LookupEffect(device, id);
509 if(!effect)
510 throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", id};
512 device->mEffectNames.insert_or_assign(id, name);
516 EffectSubList::~EffectSubList()
518 if(!Effects)
519 return;
521 uint64_t usemask{~FreeMask};
522 while(usemask)
524 const int idx{al::countr_zero(usemask)};
525 std::destroy_at(al::to_address(Effects->begin()+idx));
526 usemask &= ~(1_u64 << idx);
528 FreeMask = ~usemask;
529 SubListAllocator{}.deallocate(Effects, 1);
530 Effects = nullptr;
534 struct EffectPreset {
535 const char name[32]; /* NOLINT(*-avoid-c-arrays) */
536 EFXEAXREVERBPROPERTIES props;
538 #define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x}
539 static constexpr std::array reverblist{
540 DECL(GENERIC),
541 DECL(PADDEDCELL),
542 DECL(ROOM),
543 DECL(BATHROOM),
544 DECL(LIVINGROOM),
545 DECL(STONEROOM),
546 DECL(AUDITORIUM),
547 DECL(CONCERTHALL),
548 DECL(CAVE),
549 DECL(ARENA),
550 DECL(HANGAR),
551 DECL(CARPETEDHALLWAY),
552 DECL(HALLWAY),
553 DECL(STONECORRIDOR),
554 DECL(ALLEY),
555 DECL(FOREST),
556 DECL(CITY),
557 DECL(MOUNTAINS),
558 DECL(QUARRY),
559 DECL(PLAIN),
560 DECL(PARKINGLOT),
561 DECL(SEWERPIPE),
562 DECL(UNDERWATER),
563 DECL(DRUGGED),
564 DECL(DIZZY),
565 DECL(PSYCHOTIC),
567 DECL(CASTLE_SMALLROOM),
568 DECL(CASTLE_SHORTPASSAGE),
569 DECL(CASTLE_MEDIUMROOM),
570 DECL(CASTLE_LARGEROOM),
571 DECL(CASTLE_LONGPASSAGE),
572 DECL(CASTLE_HALL),
573 DECL(CASTLE_CUPBOARD),
574 DECL(CASTLE_COURTYARD),
575 DECL(CASTLE_ALCOVE),
577 DECL(FACTORY_SMALLROOM),
578 DECL(FACTORY_SHORTPASSAGE),
579 DECL(FACTORY_MEDIUMROOM),
580 DECL(FACTORY_LARGEROOM),
581 DECL(FACTORY_LONGPASSAGE),
582 DECL(FACTORY_HALL),
583 DECL(FACTORY_CUPBOARD),
584 DECL(FACTORY_COURTYARD),
585 DECL(FACTORY_ALCOVE),
587 DECL(ICEPALACE_SMALLROOM),
588 DECL(ICEPALACE_SHORTPASSAGE),
589 DECL(ICEPALACE_MEDIUMROOM),
590 DECL(ICEPALACE_LARGEROOM),
591 DECL(ICEPALACE_LONGPASSAGE),
592 DECL(ICEPALACE_HALL),
593 DECL(ICEPALACE_CUPBOARD),
594 DECL(ICEPALACE_COURTYARD),
595 DECL(ICEPALACE_ALCOVE),
597 DECL(SPACESTATION_SMALLROOM),
598 DECL(SPACESTATION_SHORTPASSAGE),
599 DECL(SPACESTATION_MEDIUMROOM),
600 DECL(SPACESTATION_LARGEROOM),
601 DECL(SPACESTATION_LONGPASSAGE),
602 DECL(SPACESTATION_HALL),
603 DECL(SPACESTATION_CUPBOARD),
604 DECL(SPACESTATION_ALCOVE),
606 DECL(WOODEN_SMALLROOM),
607 DECL(WOODEN_SHORTPASSAGE),
608 DECL(WOODEN_MEDIUMROOM),
609 DECL(WOODEN_LARGEROOM),
610 DECL(WOODEN_LONGPASSAGE),
611 DECL(WOODEN_HALL),
612 DECL(WOODEN_CUPBOARD),
613 DECL(WOODEN_COURTYARD),
614 DECL(WOODEN_ALCOVE),
616 DECL(SPORT_EMPTYSTADIUM),
617 DECL(SPORT_SQUASHCOURT),
618 DECL(SPORT_SMALLSWIMMINGPOOL),
619 DECL(SPORT_LARGESWIMMINGPOOL),
620 DECL(SPORT_GYMNASIUM),
621 DECL(SPORT_FULLSTADIUM),
622 DECL(SPORT_STADIUMTANNOY),
624 DECL(PREFAB_WORKSHOP),
625 DECL(PREFAB_SCHOOLROOM),
626 DECL(PREFAB_PRACTISEROOM),
627 DECL(PREFAB_OUTHOUSE),
628 DECL(PREFAB_CARAVAN),
630 DECL(DOME_TOMB),
631 DECL(PIPE_SMALL),
632 DECL(DOME_SAINTPAULS),
633 DECL(PIPE_LONGTHIN),
634 DECL(PIPE_LARGE),
635 DECL(PIPE_RESONANT),
637 DECL(OUTDOORS_BACKYARD),
638 DECL(OUTDOORS_ROLLINGPLAINS),
639 DECL(OUTDOORS_DEEPCANYON),
640 DECL(OUTDOORS_CREEK),
641 DECL(OUTDOORS_VALLEY),
643 DECL(MOOD_HEAVEN),
644 DECL(MOOD_HELL),
645 DECL(MOOD_MEMORY),
647 DECL(DRIVING_COMMENTATOR),
648 DECL(DRIVING_PITGARAGE),
649 DECL(DRIVING_INCAR_RACER),
650 DECL(DRIVING_INCAR_SPORTS),
651 DECL(DRIVING_INCAR_LUXURY),
652 DECL(DRIVING_FULLGRANDSTAND),
653 DECL(DRIVING_EMPTYGRANDSTAND),
654 DECL(DRIVING_TUNNEL),
656 DECL(CITY_STREETS),
657 DECL(CITY_SUBWAY),
658 DECL(CITY_MUSEUM),
659 DECL(CITY_LIBRARY),
660 DECL(CITY_UNDERPASS),
661 DECL(CITY_ABANDONED),
663 DECL(DUSTYROOM),
664 DECL(CHAPEL),
665 DECL(SMALLWATERROOM),
667 #undef DECL
669 void LoadReverbPreset(const std::string_view name, ALeffect *effect)
671 using namespace std::string_view_literals;
673 if(al::case_compare(name, "NONE"sv) == 0)
675 InitEffectParams(effect, AL_EFFECT_NULL);
676 TRACE("Loading reverb '%s'\n", "NONE");
677 return;
680 if(!DisabledEffects.test(EAXREVERB_EFFECT))
681 InitEffectParams(effect, AL_EFFECT_EAXREVERB);
682 else if(!DisabledEffects.test(REVERB_EFFECT))
683 InitEffectParams(effect, AL_EFFECT_REVERB);
684 else
685 InitEffectParams(effect, AL_EFFECT_NULL);
686 for(const auto &reverbitem : reverblist)
688 if(al::case_compare(name, std::data(reverbitem.name)) != 0)
689 continue;
691 TRACE("Loading reverb '%s'\n", std::data(reverbitem.name));
692 const auto &props = reverbitem.props;
693 auto &dst = std::get<ReverbProps>(effect->Props);
694 dst.Density = props.flDensity;
695 dst.Diffusion = props.flDiffusion;
696 dst.Gain = props.flGain;
697 dst.GainHF = props.flGainHF;
698 dst.GainLF = props.flGainLF;
699 dst.DecayTime = props.flDecayTime;
700 dst.DecayHFRatio = props.flDecayHFRatio;
701 dst.DecayLFRatio = props.flDecayLFRatio;
702 dst.ReflectionsGain = props.flReflectionsGain;
703 dst.ReflectionsDelay = props.flReflectionsDelay;
704 dst.ReflectionsPan[0] = props.flReflectionsPan[0];
705 dst.ReflectionsPan[1] = props.flReflectionsPan[1];
706 dst.ReflectionsPan[2] = props.flReflectionsPan[2];
707 dst.LateReverbGain = props.flLateReverbGain;
708 dst.LateReverbDelay = props.flLateReverbDelay;
709 dst.LateReverbPan[0] = props.flLateReverbPan[0];
710 dst.LateReverbPan[1] = props.flLateReverbPan[1];
711 dst.LateReverbPan[2] = props.flLateReverbPan[2];
712 dst.EchoTime = props.flEchoTime;
713 dst.EchoDepth = props.flEchoDepth;
714 dst.ModulationTime = props.flModulationTime;
715 dst.ModulationDepth = props.flModulationDepth;
716 dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF;
717 dst.HFReference = props.flHFReference;
718 dst.LFReference = props.flLFReference;
719 dst.RoomRolloffFactor = props.flRoomRolloffFactor;
720 dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE;
721 return;
724 WARN("Reverb preset '%.*s' not found\n", al::sizei(name), name.data());
727 bool IsValidEffectType(ALenum type) noexcept
729 if(type == AL_EFFECT_NULL)
730 return true;
732 auto check_effect = [type](const EffectList &item) noexcept -> bool
733 { return type == item.val && !DisabledEffects.test(item.type); };
734 return std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect);