]> git.tdb.fi Git - ext/openal.git/blob - al/effect.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / al / effect.cpp
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.
8  *
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.
13  *
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
19  */
20
21 #include "config.h"
22
23 #include "effect.h"
24
25 #include <algorithm>
26 #include <cstdint>
27 #include <cstring>
28 #include <iterator>
29 #include <memory>
30 #include <mutex>
31 #include <new>
32 #include <numeric>
33 #include <utility>
34
35 #include "AL/al.h"
36 #include "AL/alc.h"
37 #include "AL/alext.h"
38 #include "AL/efx-presets.h"
39 #include "AL/efx.h"
40
41 #include "albit.h"
42 #include "alc/context.h"
43 #include "alc/device.h"
44 #include "alc/effects/base.h"
45 #include "alc/inprogext.h"
46 #include "almalloc.h"
47 #include "alnumeric.h"
48 #include "alstring.h"
49 #include "core/except.h"
50 #include "core/logging.h"
51 #include "opthelpers.h"
52 #include "vector.h"
53
54 #ifdef ALSOFT_EAX
55 #include <cassert>
56
57 #include "eax/exception.h"
58 #endif // ALSOFT_EAX
59
60 const EffectList gEffectList[16]{
61     { "eaxreverb",   EAXREVERB_EFFECT,   AL_EFFECT_EAXREVERB },
62     { "reverb",      REVERB_EFFECT,      AL_EFFECT_REVERB },
63     { "autowah",     AUTOWAH_EFFECT,     AL_EFFECT_AUTOWAH },
64     { "chorus",      CHORUS_EFFECT,      AL_EFFECT_CHORUS },
65     { "compressor",  COMPRESSOR_EFFECT,  AL_EFFECT_COMPRESSOR },
66     { "distortion",  DISTORTION_EFFECT,  AL_EFFECT_DISTORTION },
67     { "echo",        ECHO_EFFECT,        AL_EFFECT_ECHO },
68     { "equalizer",   EQUALIZER_EFFECT,   AL_EFFECT_EQUALIZER },
69     { "flanger",     FLANGER_EFFECT,     AL_EFFECT_FLANGER },
70     { "fshifter",    FSHIFTER_EFFECT,    AL_EFFECT_FREQUENCY_SHIFTER },
71     { "modulator",   MODULATOR_EFFECT,   AL_EFFECT_RING_MODULATOR },
72     { "pshifter",    PSHIFTER_EFFECT,    AL_EFFECT_PITCH_SHIFTER },
73     { "vmorpher",    VMORPHER_EFFECT,    AL_EFFECT_VOCAL_MORPHER },
74     { "dedicated",   DEDICATED_EFFECT,   AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
75     { "dedicated",   DEDICATED_EFFECT,   AL_EFFECT_DEDICATED_DIALOGUE },
76     { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_REVERB_SOFT },
77 };
78
79 bool DisabledEffects[MAX_EFFECTS];
80
81
82 effect_exception::effect_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
83 {
84     std::va_list args;
85     va_start(args, msg);
86     setMessage(msg, args);
87     va_end(args);
88 }
89 effect_exception::~effect_exception() = default;
90
91 namespace {
92
93 struct EffectPropsItem {
94     ALenum Type;
95     const EffectProps &DefaultProps;
96     const EffectVtable &Vtable;
97 };
98 constexpr EffectPropsItem EffectPropsList[] = {
99     { AL_EFFECT_NULL, NullEffectProps, NullEffectVtable },
100     { AL_EFFECT_EAXREVERB, ReverbEffectProps, ReverbEffectVtable },
101     { AL_EFFECT_REVERB, StdReverbEffectProps, StdReverbEffectVtable },
102     { AL_EFFECT_AUTOWAH, AutowahEffectProps, AutowahEffectVtable },
103     { AL_EFFECT_CHORUS, ChorusEffectProps, ChorusEffectVtable },
104     { AL_EFFECT_COMPRESSOR, CompressorEffectProps, CompressorEffectVtable },
105     { AL_EFFECT_DISTORTION, DistortionEffectProps, DistortionEffectVtable },
106     { AL_EFFECT_ECHO, EchoEffectProps, EchoEffectVtable },
107     { AL_EFFECT_EQUALIZER, EqualizerEffectProps, EqualizerEffectVtable },
108     { AL_EFFECT_FLANGER, FlangerEffectProps, FlangerEffectVtable },
109     { AL_EFFECT_FREQUENCY_SHIFTER, FshifterEffectProps, FshifterEffectVtable },
110     { AL_EFFECT_RING_MODULATOR, ModulatorEffectProps, ModulatorEffectVtable },
111     { AL_EFFECT_PITCH_SHIFTER, PshifterEffectProps, PshifterEffectVtable },
112     { AL_EFFECT_VOCAL_MORPHER, VmorpherEffectProps, VmorpherEffectVtable },
113     { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedEffectProps, DedicatedEffectVtable },
114     { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedEffectProps, DedicatedEffectVtable },
115     { AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable },
116 };
117
118
119 void ALeffect_setParami(ALeffect *effect, ALenum param, int value)
120 { effect->vtab->setParami(&effect->Props, param, value); }
121 void ALeffect_setParamiv(ALeffect *effect, ALenum param, const int *values)
122 { effect->vtab->setParamiv(&effect->Props, param, values); }
123 void ALeffect_setParamf(ALeffect *effect, ALenum param, float value)
124 { effect->vtab->setParamf(&effect->Props, param, value); }
125 void ALeffect_setParamfv(ALeffect *effect, ALenum param, const float *values)
126 { effect->vtab->setParamfv(&effect->Props, param, values); }
127
128 void ALeffect_getParami(const ALeffect *effect, ALenum param, int *value)
129 { effect->vtab->getParami(&effect->Props, param, value); }
130 void ALeffect_getParamiv(const ALeffect *effect, ALenum param, int *values)
131 { effect->vtab->getParamiv(&effect->Props, param, values); }
132 void ALeffect_getParamf(const ALeffect *effect, ALenum param, float *value)
133 { effect->vtab->getParamf(&effect->Props, param, value); }
134 void ALeffect_getParamfv(const ALeffect *effect, ALenum param, float *values)
135 { effect->vtab->getParamfv(&effect->Props, param, values); }
136
137
138 const EffectPropsItem *getEffectPropsItemByType(ALenum type)
139 {
140     auto iter = std::find_if(std::begin(EffectPropsList), std::end(EffectPropsList),
141         [type](const EffectPropsItem &item) noexcept -> bool
142         { return item.Type == type; });
143     return (iter != std::end(EffectPropsList)) ? al::to_address(iter) : nullptr;
144 }
145
146 void InitEffectParams(ALeffect *effect, ALenum type)
147 {
148     const EffectPropsItem *item{getEffectPropsItemByType(type)};
149     if(item)
150     {
151         effect->Props = item->DefaultProps;
152         effect->vtab = &item->Vtable;
153     }
154     else
155     {
156         effect->Props = EffectProps{};
157         effect->vtab = &NullEffectVtable;
158     }
159     effect->type = type;
160 }
161
162 bool EnsureEffects(ALCdevice *device, size_t needed)
163 {
164     size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0},
165         [](size_t cur, const EffectSubList &sublist) noexcept -> size_t
166         { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
167
168     while(needed > count)
169     {
170         if(device->EffectList.size() >= 1<<25) UNLIKELY
171             return false;
172
173         device->EffectList.emplace_back();
174         auto sublist = device->EffectList.end() - 1;
175         sublist->FreeMask = ~0_u64;
176         sublist->Effects = static_cast<ALeffect*>(al_calloc(alignof(ALeffect), sizeof(ALeffect)*64));
177         if(!sublist->Effects) UNLIKELY
178         {
179             device->EffectList.pop_back();
180             return false;
181         }
182         count += 64;
183     }
184     return true;
185 }
186
187 ALeffect *AllocEffect(ALCdevice *device)
188 {
189     auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
190         [](const EffectSubList &entry) noexcept -> bool
191         { return entry.FreeMask != 0; });
192     auto lidx = static_cast<ALuint>(std::distance(device->EffectList.begin(), sublist));
193     auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
194     ASSUME(slidx < 64);
195
196     ALeffect *effect{al::construct_at(sublist->Effects + slidx)};
197     InitEffectParams(effect, AL_EFFECT_NULL);
198
199     /* Add 1 to avoid effect ID 0. */
200     effect->id = ((lidx<<6) | slidx) + 1;
201
202     sublist->FreeMask &= ~(1_u64 << slidx);
203
204     return effect;
205 }
206
207 void FreeEffect(ALCdevice *device, ALeffect *effect)
208 {
209     const ALuint id{effect->id - 1};
210     const size_t lidx{id >> 6};
211     const ALuint slidx{id & 0x3f};
212
213     al::destroy_at(effect);
214
215     device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
216 }
217
218 inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
219 {
220     const size_t lidx{(id-1) >> 6};
221     const ALuint slidx{(id-1) & 0x3f};
222
223     if(lidx >= device->EffectList.size()) UNLIKELY
224         return nullptr;
225     EffectSubList &sublist = device->EffectList[lidx];
226     if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
227         return nullptr;
228     return sublist.Effects + slidx;
229 }
230
231 } // namespace
232
233 AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
234 START_API_FUNC
235 {
236     ContextRef context{GetContextRef()};
237     if(!context) UNLIKELY return;
238
239     if(n < 0) UNLIKELY
240         context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
241     if(n <= 0) UNLIKELY return;
242
243     ALCdevice *device{context->mALDevice.get()};
244     std::lock_guard<std::mutex> _{device->EffectLock};
245     if(!EnsureEffects(device, static_cast<ALuint>(n)))
246     {
247         context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, (n==1)?"":"s");
248         return;
249     }
250
251     if(n == 1) LIKELY
252     {
253         /* Special handling for the easy and normal case. */
254         ALeffect *effect{AllocEffect(device)};
255         effects[0] = effect->id;
256     }
257     else
258     {
259         /* Store the allocated buffer IDs in a separate local list, to avoid
260          * modifying the user storage in case of failure.
261          */
262         al::vector<ALuint> ids;
263         ids.reserve(static_cast<ALuint>(n));
264         do {
265             ALeffect *effect{AllocEffect(device)};
266             ids.emplace_back(effect->id);
267         } while(--n);
268         std::copy(ids.cbegin(), ids.cend(), effects);
269     }
270 }
271 END_API_FUNC
272
273 AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
274 START_API_FUNC
275 {
276     ContextRef context{GetContextRef()};
277     if(!context) UNLIKELY return;
278
279     if(n < 0) UNLIKELY
280         context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
281     if(n <= 0) UNLIKELY return;
282
283     ALCdevice *device{context->mALDevice.get()};
284     std::lock_guard<std::mutex> _{device->EffectLock};
285
286     /* First try to find any effects that are invalid. */
287     auto validate_effect = [device](const ALuint eid) -> bool
288     { return !eid || LookupEffect(device, eid) != nullptr; };
289
290     const ALuint *effects_end = effects + n;
291     auto inveffect = std::find_if_not(effects, effects_end, validate_effect);
292     if(inveffect != effects_end) UNLIKELY
293     {
294         context->setError(AL_INVALID_NAME, "Invalid effect ID %u", *inveffect);
295         return;
296     }
297
298     /* All good. Delete non-0 effect IDs. */
299     auto delete_effect = [device](ALuint eid) -> void
300     {
301         ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr};
302         if(effect) FreeEffect(device, effect);
303     };
304     std::for_each(effects, effects_end, delete_effect);
305 }
306 END_API_FUNC
307
308 AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
309 START_API_FUNC
310 {
311     ContextRef context{GetContextRef()};
312     if(context) LIKELY
313     {
314         ALCdevice *device{context->mALDevice.get()};
315         std::lock_guard<std::mutex> _{device->EffectLock};
316         if(!effect || LookupEffect(device, effect))
317             return AL_TRUE;
318     }
319     return AL_FALSE;
320 }
321 END_API_FUNC
322
323 AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
324 START_API_FUNC
325 {
326     ContextRef context{GetContextRef()};
327     if(!context) UNLIKELY return;
328
329     ALCdevice *device{context->mALDevice.get()};
330     std::lock_guard<std::mutex> _{device->EffectLock};
331
332     ALeffect *aleffect{LookupEffect(device, effect)};
333     if(!aleffect) UNLIKELY
334         context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
335     else if(param == AL_EFFECT_TYPE)
336     {
337         bool isOk{value == AL_EFFECT_NULL};
338         if(!isOk)
339         {
340             for(const EffectList &effectitem : gEffectList)
341             {
342                 if(value == effectitem.val && !DisabledEffects[effectitem.type])
343                 {
344                     isOk = true;
345                     break;
346                 }
347             }
348         }
349
350         if(isOk)
351             InitEffectParams(aleffect, value);
352         else
353             context->setError(AL_INVALID_VALUE, "Effect type 0x%04x not supported", value);
354     }
355     else try
356     {
357         /* Call the appropriate handler */
358         ALeffect_setParami(aleffect, param, value);
359     }
360     catch(effect_exception &e) {
361         context->setError(e.errorCode(), "%s", e.what());
362     }
363 }
364 END_API_FUNC
365
366 AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values)
367 START_API_FUNC
368 {
369     switch(param)
370     {
371     case AL_EFFECT_TYPE:
372         alEffecti(effect, param, values[0]);
373         return;
374     }
375
376     ContextRef context{GetContextRef()};
377     if(!context) UNLIKELY return;
378
379     ALCdevice *device{context->mALDevice.get()};
380     std::lock_guard<std::mutex> _{device->EffectLock};
381
382     ALeffect *aleffect{LookupEffect(device, effect)};
383     if(!aleffect) UNLIKELY
384         context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
385     else try
386     {
387         /* Call the appropriate handler */
388         ALeffect_setParamiv(aleffect, param, values);
389     }
390     catch(effect_exception &e) {
391         context->setError(e.errorCode(), "%s", e.what());
392     }
393 }
394 END_API_FUNC
395
396 AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
397 START_API_FUNC
398 {
399     ContextRef context{GetContextRef()};
400     if(!context) UNLIKELY return;
401
402     ALCdevice *device{context->mALDevice.get()};
403     std::lock_guard<std::mutex> _{device->EffectLock};
404
405     ALeffect *aleffect{LookupEffect(device, effect)};
406     if(!aleffect) UNLIKELY
407         context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
408     else try
409     {
410         /* Call the appropriate handler */
411         ALeffect_setParamf(aleffect, param, value);
412     }
413     catch(effect_exception &e) {
414         context->setError(e.errorCode(), "%s", e.what());
415     }
416 }
417 END_API_FUNC
418
419 AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
420 START_API_FUNC
421 {
422     ContextRef context{GetContextRef()};
423     if(!context) UNLIKELY return;
424
425     ALCdevice *device{context->mALDevice.get()};
426     std::lock_guard<std::mutex> _{device->EffectLock};
427
428     ALeffect *aleffect{LookupEffect(device, effect)};
429     if(!aleffect) UNLIKELY
430         context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
431     else try
432     {
433         /* Call the appropriate handler */
434         ALeffect_setParamfv(aleffect, param, values);
435     }
436     catch(effect_exception &e) {
437         context->setError(e.errorCode(), "%s", e.what());
438     }
439 }
440 END_API_FUNC
441
442 AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value)
443 START_API_FUNC
444 {
445     ContextRef context{GetContextRef()};
446     if(!context) UNLIKELY return;
447
448     ALCdevice *device{context->mALDevice.get()};
449     std::lock_guard<std::mutex> _{device->EffectLock};
450
451     const ALeffect *aleffect{LookupEffect(device, effect)};
452     if(!aleffect) UNLIKELY
453         context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
454     else if(param == AL_EFFECT_TYPE)
455         *value = aleffect->type;
456     else try
457     {
458         /* Call the appropriate handler */
459         ALeffect_getParami(aleffect, param, value);
460     }
461     catch(effect_exception &e) {
462         context->setError(e.errorCode(), "%s", e.what());
463     }
464 }
465 END_API_FUNC
466
467 AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values)
468 START_API_FUNC
469 {
470     switch(param)
471     {
472     case AL_EFFECT_TYPE:
473         alGetEffecti(effect, param, values);
474         return;
475     }
476
477     ContextRef context{GetContextRef()};
478     if(!context) UNLIKELY return;
479
480     ALCdevice *device{context->mALDevice.get()};
481     std::lock_guard<std::mutex> _{device->EffectLock};
482
483     const ALeffect *aleffect{LookupEffect(device, effect)};
484     if(!aleffect) UNLIKELY
485         context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
486     else try
487     {
488         /* Call the appropriate handler */
489         ALeffect_getParamiv(aleffect, param, values);
490     }
491     catch(effect_exception &e) {
492         context->setError(e.errorCode(), "%s", e.what());
493     }
494 }
495 END_API_FUNC
496
497 AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value)
498 START_API_FUNC
499 {
500     ContextRef context{GetContextRef()};
501     if(!context) UNLIKELY return;
502
503     ALCdevice *device{context->mALDevice.get()};
504     std::lock_guard<std::mutex> _{device->EffectLock};
505
506     const ALeffect *aleffect{LookupEffect(device, effect)};
507     if(!aleffect) UNLIKELY
508         context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
509     else try
510     {
511         /* Call the appropriate handler */
512         ALeffect_getParamf(aleffect, param, value);
513     }
514     catch(effect_exception &e) {
515         context->setError(e.errorCode(), "%s", e.what());
516     }
517 }
518 END_API_FUNC
519
520 AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values)
521 START_API_FUNC
522 {
523     ContextRef context{GetContextRef()};
524     if(!context) UNLIKELY return;
525
526     ALCdevice *device{context->mALDevice.get()};
527     std::lock_guard<std::mutex> _{device->EffectLock};
528
529     const ALeffect *aleffect{LookupEffect(device, effect)};
530     if(!aleffect) UNLIKELY
531         context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
532     else try
533     {
534         /* Call the appropriate handler */
535         ALeffect_getParamfv(aleffect, param, values);
536     }
537     catch(effect_exception &e) {
538         context->setError(e.errorCode(), "%s", e.what());
539     }
540 }
541 END_API_FUNC
542
543
544 void InitEffect(ALeffect *effect)
545 {
546     InitEffectParams(effect, AL_EFFECT_NULL);
547 }
548
549 EffectSubList::~EffectSubList()
550 {
551     uint64_t usemask{~FreeMask};
552     while(usemask)
553     {
554         const int idx{al::countr_zero(usemask)};
555         al::destroy_at(Effects+idx);
556         usemask &= ~(1_u64 << idx);
557     }
558     FreeMask = ~usemask;
559     al_free(Effects);
560     Effects = nullptr;
561 }
562
563
564 #define DECL(x) { #x, EFX_REVERB_PRESET_##x }
565 static const struct {
566     const char name[32];
567     EFXEAXREVERBPROPERTIES props;
568 } reverblist[] = {
569     DECL(GENERIC),
570     DECL(PADDEDCELL),
571     DECL(ROOM),
572     DECL(BATHROOM),
573     DECL(LIVINGROOM),
574     DECL(STONEROOM),
575     DECL(AUDITORIUM),
576     DECL(CONCERTHALL),
577     DECL(CAVE),
578     DECL(ARENA),
579     DECL(HANGAR),
580     DECL(CARPETEDHALLWAY),
581     DECL(HALLWAY),
582     DECL(STONECORRIDOR),
583     DECL(ALLEY),
584     DECL(FOREST),
585     DECL(CITY),
586     DECL(MOUNTAINS),
587     DECL(QUARRY),
588     DECL(PLAIN),
589     DECL(PARKINGLOT),
590     DECL(SEWERPIPE),
591     DECL(UNDERWATER),
592     DECL(DRUGGED),
593     DECL(DIZZY),
594     DECL(PSYCHOTIC),
595
596     DECL(CASTLE_SMALLROOM),
597     DECL(CASTLE_SHORTPASSAGE),
598     DECL(CASTLE_MEDIUMROOM),
599     DECL(CASTLE_LARGEROOM),
600     DECL(CASTLE_LONGPASSAGE),
601     DECL(CASTLE_HALL),
602     DECL(CASTLE_CUPBOARD),
603     DECL(CASTLE_COURTYARD),
604     DECL(CASTLE_ALCOVE),
605
606     DECL(FACTORY_SMALLROOM),
607     DECL(FACTORY_SHORTPASSAGE),
608     DECL(FACTORY_MEDIUMROOM),
609     DECL(FACTORY_LARGEROOM),
610     DECL(FACTORY_LONGPASSAGE),
611     DECL(FACTORY_HALL),
612     DECL(FACTORY_CUPBOARD),
613     DECL(FACTORY_COURTYARD),
614     DECL(FACTORY_ALCOVE),
615
616     DECL(ICEPALACE_SMALLROOM),
617     DECL(ICEPALACE_SHORTPASSAGE),
618     DECL(ICEPALACE_MEDIUMROOM),
619     DECL(ICEPALACE_LARGEROOM),
620     DECL(ICEPALACE_LONGPASSAGE),
621     DECL(ICEPALACE_HALL),
622     DECL(ICEPALACE_CUPBOARD),
623     DECL(ICEPALACE_COURTYARD),
624     DECL(ICEPALACE_ALCOVE),
625
626     DECL(SPACESTATION_SMALLROOM),
627     DECL(SPACESTATION_SHORTPASSAGE),
628     DECL(SPACESTATION_MEDIUMROOM),
629     DECL(SPACESTATION_LARGEROOM),
630     DECL(SPACESTATION_LONGPASSAGE),
631     DECL(SPACESTATION_HALL),
632     DECL(SPACESTATION_CUPBOARD),
633     DECL(SPACESTATION_ALCOVE),
634
635     DECL(WOODEN_SMALLROOM),
636     DECL(WOODEN_SHORTPASSAGE),
637     DECL(WOODEN_MEDIUMROOM),
638     DECL(WOODEN_LARGEROOM),
639     DECL(WOODEN_LONGPASSAGE),
640     DECL(WOODEN_HALL),
641     DECL(WOODEN_CUPBOARD),
642     DECL(WOODEN_COURTYARD),
643     DECL(WOODEN_ALCOVE),
644
645     DECL(SPORT_EMPTYSTADIUM),
646     DECL(SPORT_SQUASHCOURT),
647     DECL(SPORT_SMALLSWIMMINGPOOL),
648     DECL(SPORT_LARGESWIMMINGPOOL),
649     DECL(SPORT_GYMNASIUM),
650     DECL(SPORT_FULLSTADIUM),
651     DECL(SPORT_STADIUMTANNOY),
652
653     DECL(PREFAB_WORKSHOP),
654     DECL(PREFAB_SCHOOLROOM),
655     DECL(PREFAB_PRACTISEROOM),
656     DECL(PREFAB_OUTHOUSE),
657     DECL(PREFAB_CARAVAN),
658
659     DECL(DOME_TOMB),
660     DECL(PIPE_SMALL),
661     DECL(DOME_SAINTPAULS),
662     DECL(PIPE_LONGTHIN),
663     DECL(PIPE_LARGE),
664     DECL(PIPE_RESONANT),
665
666     DECL(OUTDOORS_BACKYARD),
667     DECL(OUTDOORS_ROLLINGPLAINS),
668     DECL(OUTDOORS_DEEPCANYON),
669     DECL(OUTDOORS_CREEK),
670     DECL(OUTDOORS_VALLEY),
671
672     DECL(MOOD_HEAVEN),
673     DECL(MOOD_HELL),
674     DECL(MOOD_MEMORY),
675
676     DECL(DRIVING_COMMENTATOR),
677     DECL(DRIVING_PITGARAGE),
678     DECL(DRIVING_INCAR_RACER),
679     DECL(DRIVING_INCAR_SPORTS),
680     DECL(DRIVING_INCAR_LUXURY),
681     DECL(DRIVING_FULLGRANDSTAND),
682     DECL(DRIVING_EMPTYGRANDSTAND),
683     DECL(DRIVING_TUNNEL),
684
685     DECL(CITY_STREETS),
686     DECL(CITY_SUBWAY),
687     DECL(CITY_MUSEUM),
688     DECL(CITY_LIBRARY),
689     DECL(CITY_UNDERPASS),
690     DECL(CITY_ABANDONED),
691
692     DECL(DUSTYROOM),
693     DECL(CHAPEL),
694     DECL(SMALLWATERROOM),
695 };
696 #undef DECL
697
698 void LoadReverbPreset(const char *name, ALeffect *effect)
699 {
700     if(al::strcasecmp(name, "NONE") == 0)
701     {
702         InitEffectParams(effect, AL_EFFECT_NULL);
703         TRACE("Loading reverb '%s'\n", "NONE");
704         return;
705     }
706
707     if(!DisabledEffects[EAXREVERB_EFFECT])
708         InitEffectParams(effect, AL_EFFECT_EAXREVERB);
709     else if(!DisabledEffects[REVERB_EFFECT])
710         InitEffectParams(effect, AL_EFFECT_REVERB);
711     else
712         InitEffectParams(effect, AL_EFFECT_NULL);
713     for(const auto &reverbitem : reverblist)
714     {
715         const EFXEAXREVERBPROPERTIES *props;
716
717         if(al::strcasecmp(name, reverbitem.name) != 0)
718             continue;
719
720         TRACE("Loading reverb '%s'\n", reverbitem.name);
721         props = &reverbitem.props;
722         effect->Props.Reverb.Density   = props->flDensity;
723         effect->Props.Reverb.Diffusion = props->flDiffusion;
724         effect->Props.Reverb.Gain   = props->flGain;
725         effect->Props.Reverb.GainHF = props->flGainHF;
726         effect->Props.Reverb.GainLF = props->flGainLF;
727         effect->Props.Reverb.DecayTime    = props->flDecayTime;
728         effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio;
729         effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio;
730         effect->Props.Reverb.ReflectionsGain   = props->flReflectionsGain;
731         effect->Props.Reverb.ReflectionsDelay  = props->flReflectionsDelay;
732         effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0];
733         effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1];
734         effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2];
735         effect->Props.Reverb.LateReverbGain   = props->flLateReverbGain;
736         effect->Props.Reverb.LateReverbDelay  = props->flLateReverbDelay;
737         effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0];
738         effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1];
739         effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2];
740         effect->Props.Reverb.EchoTime  = props->flEchoTime;
741         effect->Props.Reverb.EchoDepth = props->flEchoDepth;
742         effect->Props.Reverb.ModulationTime  = props->flModulationTime;
743         effect->Props.Reverb.ModulationDepth = props->flModulationDepth;
744         effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF;
745         effect->Props.Reverb.HFReference = props->flHFReference;
746         effect->Props.Reverb.LFReference = props->flLFReference;
747         effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor;
748         effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit ? AL_TRUE : AL_FALSE;
749         return;
750     }
751
752     WARN("Reverb preset '%s' not found\n", name);
753 }
754
755 bool IsValidEffectType(ALenum type) noexcept
756 {
757     if(type == AL_EFFECT_NULL)
758         return true;
759
760     for(const auto &effect_item : gEffectList)
761     {
762         if(type == effect_item.val && !DisabledEffects[effect_item.type])
763             return true;
764     }
765     return false;
766 }