]> git.tdb.fi Git - ext/openal.git/blob - al/filter.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / al / filter.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 "filter.h"
24
25 #include <algorithm>
26 #include <cstdarg>
27 #include <cstdint>
28 #include <cstdio>
29 #include <iterator>
30 #include <memory>
31 #include <mutex>
32 #include <new>
33 #include <numeric>
34
35 #include "AL/al.h"
36 #include "AL/alc.h"
37 #include "AL/efx.h"
38
39 #include "albit.h"
40 #include "alc/context.h"
41 #include "alc/device.h"
42 #include "almalloc.h"
43 #include "alnumeric.h"
44 #include "core/except.h"
45 #include "opthelpers.h"
46 #include "vector.h"
47
48
49 namespace {
50
51 class filter_exception final : public al::base_exception {
52     ALenum mErrorCode;
53
54 public:
55 #ifdef __USE_MINGW_ANSI_STDIO
56     [[gnu::format(gnu_printf, 3, 4)]]
57 #else
58     [[gnu::format(printf, 3, 4)]]
59 #endif
60     filter_exception(ALenum code, const char *msg, ...);
61     ~filter_exception() override;
62
63     ALenum errorCode() const noexcept { return mErrorCode; }
64 };
65
66 filter_exception::filter_exception(ALenum code, const char* msg, ...) : mErrorCode{code}
67 {
68     std::va_list args;
69     va_start(args, msg);
70     setMessage(msg, args);
71     va_end(args);
72 }
73 filter_exception::~filter_exception() = default;
74
75
76 #define DEFINE_ALFILTER_VTABLE(T)                                  \
77 const ALfilter::Vtable T##_vtable = {                              \
78     T##_setParami, T##_setParamiv, T##_setParamf, T##_setParamfv,  \
79     T##_getParami, T##_getParamiv, T##_getParamf, T##_getParamfv,  \
80 }
81
82 void ALlowpass_setParami(ALfilter*, ALenum param, int)
83 { throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
84 void ALlowpass_setParamiv(ALfilter*, ALenum param, const int*)
85 {
86     throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
87         param};
88 }
89 void ALlowpass_setParamf(ALfilter *filter, ALenum param, float val)
90 {
91     switch(param)
92     {
93     case AL_LOWPASS_GAIN:
94         if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
95             throw filter_exception{AL_INVALID_VALUE, "Low-pass gain %f out of range", val};
96         filter->Gain = val;
97         break;
98
99     case AL_LOWPASS_GAINHF:
100         if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
101             throw filter_exception{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val};
102         filter->GainHF = val;
103         break;
104
105     default:
106         throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
107     }
108 }
109 void ALlowpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
110 { ALlowpass_setParamf(filter, param, vals[0]); }
111
112 void ALlowpass_getParami(const ALfilter*, ALenum param, int*)
113 { throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
114 void ALlowpass_getParamiv(const ALfilter*, ALenum param, int*)
115 {
116     throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
117         param};
118 }
119 void ALlowpass_getParamf(const ALfilter *filter, ALenum param, float *val)
120 {
121     switch(param)
122     {
123     case AL_LOWPASS_GAIN:
124         *val = filter->Gain;
125         break;
126
127     case AL_LOWPASS_GAINHF:
128         *val = filter->GainHF;
129         break;
130
131     default:
132         throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
133     }
134 }
135 void ALlowpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
136 { ALlowpass_getParamf(filter, param, vals); }
137
138 DEFINE_ALFILTER_VTABLE(ALlowpass);
139
140
141 void ALhighpass_setParami(ALfilter*, ALenum param, int)
142 { throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
143 void ALhighpass_setParamiv(ALfilter*, ALenum param, const int*)
144 {
145     throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
146         param};
147 }
148 void ALhighpass_setParamf(ALfilter *filter, ALenum param, float val)
149 {
150     switch(param)
151     {
152     case AL_HIGHPASS_GAIN:
153         if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
154             throw filter_exception{AL_INVALID_VALUE, "High-pass gain %f out of range", val};
155         filter->Gain = val;
156         break;
157
158     case AL_HIGHPASS_GAINLF:
159         if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
160             throw filter_exception{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val};
161         filter->GainLF = val;
162         break;
163
164     default:
165         throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
166     }
167 }
168 void ALhighpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
169 { ALhighpass_setParamf(filter, param, vals[0]); }
170
171 void ALhighpass_getParami(const ALfilter*, ALenum param, int*)
172 { throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
173 void ALhighpass_getParamiv(const ALfilter*, ALenum param, int*)
174 {
175     throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
176         param};
177 }
178 void ALhighpass_getParamf(const ALfilter *filter, ALenum param, float *val)
179 {
180     switch(param)
181     {
182     case AL_HIGHPASS_GAIN:
183         *val = filter->Gain;
184         break;
185
186     case AL_HIGHPASS_GAINLF:
187         *val = filter->GainLF;
188         break;
189
190     default:
191         throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
192     }
193 }
194 void ALhighpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
195 { ALhighpass_getParamf(filter, param, vals); }
196
197 DEFINE_ALFILTER_VTABLE(ALhighpass);
198
199
200 void ALbandpass_setParami(ALfilter*, ALenum param, int)
201 { throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
202 void ALbandpass_setParamiv(ALfilter*, ALenum param, const int*)
203 {
204     throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
205         param};
206 }
207 void ALbandpass_setParamf(ALfilter *filter, ALenum param, float val)
208 {
209     switch(param)
210     {
211     case AL_BANDPASS_GAIN:
212         if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
213             throw filter_exception{AL_INVALID_VALUE, "Band-pass gain %f out of range", val};
214         filter->Gain = val;
215         break;
216
217     case AL_BANDPASS_GAINHF:
218         if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
219             throw filter_exception{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val};
220         filter->GainHF = val;
221         break;
222
223     case AL_BANDPASS_GAINLF:
224         if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
225             throw filter_exception{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val};
226         filter->GainLF = val;
227         break;
228
229     default:
230         throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
231     }
232 }
233 void ALbandpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
234 { ALbandpass_setParamf(filter, param, vals[0]); }
235
236 void ALbandpass_getParami(const ALfilter*, ALenum param, int*)
237 { throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
238 void ALbandpass_getParamiv(const ALfilter*, ALenum param, int*)
239 {
240     throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
241         param};
242 }
243 void ALbandpass_getParamf(const ALfilter *filter, ALenum param, float *val)
244 {
245     switch(param)
246     {
247     case AL_BANDPASS_GAIN:
248         *val = filter->Gain;
249         break;
250
251     case AL_BANDPASS_GAINHF:
252         *val = filter->GainHF;
253         break;
254
255     case AL_BANDPASS_GAINLF:
256         *val = filter->GainLF;
257         break;
258
259     default:
260         throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
261     }
262 }
263 void ALbandpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
264 { ALbandpass_getParamf(filter, param, vals); }
265
266 DEFINE_ALFILTER_VTABLE(ALbandpass);
267
268
269 void ALnullfilter_setParami(ALfilter*, ALenum param, int)
270 { throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
271 void ALnullfilter_setParamiv(ALfilter*, ALenum param, const int*)
272 { throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
273 void ALnullfilter_setParamf(ALfilter*, ALenum param, float)
274 { throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
275 void ALnullfilter_setParamfv(ALfilter*, ALenum param, const float*)
276 { throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
277
278 void ALnullfilter_getParami(const ALfilter*, ALenum param, int*)
279 { throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
280 void ALnullfilter_getParamiv(const ALfilter*, ALenum param, int*)
281 { throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
282 void ALnullfilter_getParamf(const ALfilter*, ALenum param, float*)
283 { throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
284 void ALnullfilter_getParamfv(const ALfilter*, ALenum param, float*)
285 { throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
286
287 DEFINE_ALFILTER_VTABLE(ALnullfilter);
288
289
290 void InitFilterParams(ALfilter *filter, ALenum type)
291 {
292     if(type == AL_FILTER_LOWPASS)
293     {
294         filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
295         filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
296         filter->HFReference = LOWPASSFREQREF;
297         filter->GainLF = 1.0f;
298         filter->LFReference = HIGHPASSFREQREF;
299         filter->vtab = &ALlowpass_vtable;
300     }
301     else if(type == AL_FILTER_HIGHPASS)
302     {
303         filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
304         filter->GainHF = 1.0f;
305         filter->HFReference = LOWPASSFREQREF;
306         filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
307         filter->LFReference = HIGHPASSFREQREF;
308         filter->vtab = &ALhighpass_vtable;
309     }
310     else if(type == AL_FILTER_BANDPASS)
311     {
312         filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
313         filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
314         filter->HFReference = LOWPASSFREQREF;
315         filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
316         filter->LFReference = HIGHPASSFREQREF;
317         filter->vtab = &ALbandpass_vtable;
318     }
319     else
320     {
321         filter->Gain = 1.0f;
322         filter->GainHF = 1.0f;
323         filter->HFReference = LOWPASSFREQREF;
324         filter->GainLF = 1.0f;
325         filter->LFReference = HIGHPASSFREQREF;
326         filter->vtab = &ALnullfilter_vtable;
327     }
328     filter->type = type;
329 }
330
331 bool EnsureFilters(ALCdevice *device, size_t needed)
332 {
333     size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), size_t{0},
334         [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
335         { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
336
337     while(needed > count)
338     {
339         if(device->FilterList.size() >= 1<<25) UNLIKELY
340             return false;
341
342         device->FilterList.emplace_back();
343         auto sublist = device->FilterList.end() - 1;
344         sublist->FreeMask = ~0_u64;
345         sublist->Filters = static_cast<ALfilter*>(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64));
346         if(!sublist->Filters) UNLIKELY
347         {
348             device->FilterList.pop_back();
349             return false;
350         }
351         count += 64;
352     }
353     return true;
354 }
355
356
357 ALfilter *AllocFilter(ALCdevice *device)
358 {
359     auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
360         [](const FilterSubList &entry) noexcept -> bool
361         { return entry.FreeMask != 0; });
362     auto lidx = static_cast<ALuint>(std::distance(device->FilterList.begin(), sublist));
363     auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
364     ASSUME(slidx < 64);
365
366     ALfilter *filter{al::construct_at(sublist->Filters + slidx)};
367     InitFilterParams(filter, AL_FILTER_NULL);
368
369     /* Add 1 to avoid filter ID 0. */
370     filter->id = ((lidx<<6) | slidx) + 1;
371
372     sublist->FreeMask &= ~(1_u64 << slidx);
373
374     return filter;
375 }
376
377 void FreeFilter(ALCdevice *device, ALfilter *filter)
378 {
379     const ALuint id{filter->id - 1};
380     const size_t lidx{id >> 6};
381     const ALuint slidx{id & 0x3f};
382
383     al::destroy_at(filter);
384
385     device->FilterList[lidx].FreeMask |= 1_u64 << slidx;
386 }
387
388
389 inline ALfilter *LookupFilter(ALCdevice *device, ALuint id)
390 {
391     const size_t lidx{(id-1) >> 6};
392     const ALuint slidx{(id-1) & 0x3f};
393
394     if(lidx >= device->FilterList.size()) UNLIKELY
395         return nullptr;
396     FilterSubList &sublist = device->FilterList[lidx];
397     if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
398         return nullptr;
399     return sublist.Filters + slidx;
400 }
401
402 } // namespace
403
404 AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
405 START_API_FUNC
406 {
407     ContextRef context{GetContextRef()};
408     if(!context) UNLIKELY return;
409
410     if(n < 0) UNLIKELY
411         context->setError(AL_INVALID_VALUE, "Generating %d filters", n);
412     if(n <= 0) UNLIKELY return;
413
414     ALCdevice *device{context->mALDevice.get()};
415     std::lock_guard<std::mutex> _{device->FilterLock};
416     if(!EnsureFilters(device, static_cast<ALuint>(n)))
417     {
418         context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, (n==1)?"":"s");
419         return;
420     }
421
422     if(n == 1) LIKELY
423     {
424         /* Special handling for the easy and normal case. */
425         ALfilter *filter{AllocFilter(device)};
426         if(filter) filters[0] = filter->id;
427     }
428     else
429     {
430         /* Store the allocated buffer IDs in a separate local list, to avoid
431          * modifying the user storage in case of failure.
432          */
433         al::vector<ALuint> ids;
434         ids.reserve(static_cast<ALuint>(n));
435         do {
436             ALfilter *filter{AllocFilter(device)};
437             ids.emplace_back(filter->id);
438         } while(--n);
439         std::copy(ids.begin(), ids.end(), filters);
440     }
441 }
442 END_API_FUNC
443
444 AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
445 START_API_FUNC
446 {
447     ContextRef context{GetContextRef()};
448     if(!context) UNLIKELY return;
449
450     if(n < 0) UNLIKELY
451         context->setError(AL_INVALID_VALUE, "Deleting %d filters", n);
452     if(n <= 0) UNLIKELY return;
453
454     ALCdevice *device{context->mALDevice.get()};
455     std::lock_guard<std::mutex> _{device->FilterLock};
456
457     /* First try to find any filters that are invalid. */
458     auto validate_filter = [device](const ALuint fid) -> bool
459     { return !fid || LookupFilter(device, fid) != nullptr; };
460
461     const ALuint *filters_end = filters + n;
462     auto invflt = std::find_if_not(filters, filters_end, validate_filter);
463     if(invflt != filters_end) UNLIKELY
464     {
465         context->setError(AL_INVALID_NAME, "Invalid filter ID %u", *invflt);
466         return;
467     }
468
469     /* All good. Delete non-0 filter IDs. */
470     auto delete_filter = [device](const ALuint fid) -> void
471     {
472         ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr};
473         if(filter) FreeFilter(device, filter);
474     };
475     std::for_each(filters, filters_end, delete_filter);
476 }
477 END_API_FUNC
478
479 AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
480 START_API_FUNC
481 {
482     ContextRef context{GetContextRef()};
483     if(context) LIKELY
484     {
485         ALCdevice *device{context->mALDevice.get()};
486         std::lock_guard<std::mutex> _{device->FilterLock};
487         if(!filter || LookupFilter(device, filter))
488             return AL_TRUE;
489     }
490     return AL_FALSE;
491 }
492 END_API_FUNC
493
494
495 AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
496 START_API_FUNC
497 {
498     ContextRef context{GetContextRef()};
499     if(!context) UNLIKELY return;
500
501     ALCdevice *device{context->mALDevice.get()};
502     std::lock_guard<std::mutex> _{device->FilterLock};
503
504     ALfilter *alfilt{LookupFilter(device, filter)};
505     if(!alfilt) UNLIKELY
506         context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
507     else
508     {
509         if(param == AL_FILTER_TYPE)
510         {
511             if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
512                 || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
513                 InitFilterParams(alfilt, value);
514             else
515                 context->setError(AL_INVALID_VALUE, "Invalid filter type 0x%04x", value);
516         }
517         else try
518         {
519             /* Call the appropriate handler */
520             alfilt->setParami(param, value);
521         }
522         catch(filter_exception &e) {
523             context->setError(e.errorCode(), "%s", e.what());
524         }
525     }
526 }
527 END_API_FUNC
528
529 AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values)
530 START_API_FUNC
531 {
532     switch(param)
533     {
534     case AL_FILTER_TYPE:
535         alFilteri(filter, param, values[0]);
536         return;
537     }
538
539     ContextRef context{GetContextRef()};
540     if(!context) UNLIKELY return;
541
542     ALCdevice *device{context->mALDevice.get()};
543     std::lock_guard<std::mutex> _{device->FilterLock};
544
545     ALfilter *alfilt{LookupFilter(device, filter)};
546     if(!alfilt) UNLIKELY
547         context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
548     else try
549     {
550         /* Call the appropriate handler */
551         alfilt->setParamiv(param, values);
552     }
553     catch(filter_exception &e) {
554         context->setError(e.errorCode(), "%s", e.what());
555     }
556 }
557 END_API_FUNC
558
559 AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
560 START_API_FUNC
561 {
562     ContextRef context{GetContextRef()};
563     if(!context) UNLIKELY return;
564
565     ALCdevice *device{context->mALDevice.get()};
566     std::lock_guard<std::mutex> _{device->FilterLock};
567
568     ALfilter *alfilt{LookupFilter(device, filter)};
569     if(!alfilt) UNLIKELY
570         context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
571     else try
572     {
573         /* Call the appropriate handler */
574         alfilt->setParamf(param, value);
575     }
576     catch(filter_exception &e) {
577         context->setError(e.errorCode(), "%s", e.what());
578     }
579 }
580 END_API_FUNC
581
582 AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values)
583 START_API_FUNC
584 {
585     ContextRef context{GetContextRef()};
586     if(!context) UNLIKELY return;
587
588     ALCdevice *device{context->mALDevice.get()};
589     std::lock_guard<std::mutex> _{device->FilterLock};
590
591     ALfilter *alfilt{LookupFilter(device, filter)};
592     if(!alfilt) UNLIKELY
593         context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
594     else try
595     {
596         /* Call the appropriate handler */
597         alfilt->setParamfv(param, values);
598     }
599     catch(filter_exception &e) {
600         context->setError(e.errorCode(), "%s", e.what());
601     }
602 }
603 END_API_FUNC
604
605 AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value)
606 START_API_FUNC
607 {
608     ContextRef context{GetContextRef()};
609     if(!context) UNLIKELY return;
610
611     ALCdevice *device{context->mALDevice.get()};
612     std::lock_guard<std::mutex> _{device->FilterLock};
613
614     const ALfilter *alfilt{LookupFilter(device, filter)};
615     if(!alfilt) UNLIKELY
616         context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
617     else
618     {
619         if(param == AL_FILTER_TYPE)
620             *value = alfilt->type;
621         else try
622         {
623             /* Call the appropriate handler */
624             alfilt->getParami(param, value);
625         }
626         catch(filter_exception &e) {
627             context->setError(e.errorCode(), "%s", e.what());
628         }
629     }
630 }
631 END_API_FUNC
632
633 AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values)
634 START_API_FUNC
635 {
636     switch(param)
637     {
638     case AL_FILTER_TYPE:
639         alGetFilteri(filter, param, values);
640         return;
641     }
642
643     ContextRef context{GetContextRef()};
644     if(!context) UNLIKELY return;
645
646     ALCdevice *device{context->mALDevice.get()};
647     std::lock_guard<std::mutex> _{device->FilterLock};
648
649     const ALfilter *alfilt{LookupFilter(device, filter)};
650     if(!alfilt) UNLIKELY
651         context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
652     else try
653     {
654         /* Call the appropriate handler */
655         alfilt->getParamiv(param, values);
656     }
657     catch(filter_exception &e) {
658         context->setError(e.errorCode(), "%s", e.what());
659     }
660 }
661 END_API_FUNC
662
663 AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value)
664 START_API_FUNC
665 {
666     ContextRef context{GetContextRef()};
667     if(!context) UNLIKELY return;
668
669     ALCdevice *device{context->mALDevice.get()};
670     std::lock_guard<std::mutex> _{device->FilterLock};
671
672     const ALfilter *alfilt{LookupFilter(device, filter)};
673     if(!alfilt) UNLIKELY
674         context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
675     else try
676     {
677         /* Call the appropriate handler */
678         alfilt->getParamf(param, value);
679     }
680     catch(filter_exception &e) {
681         context->setError(e.errorCode(), "%s", e.what());
682     }
683 }
684 END_API_FUNC
685
686 AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values)
687 START_API_FUNC
688 {
689     ContextRef context{GetContextRef()};
690     if(!context) UNLIKELY return;
691
692     ALCdevice *device{context->mALDevice.get()};
693     std::lock_guard<std::mutex> _{device->FilterLock};
694
695     const ALfilter *alfilt{LookupFilter(device, filter)};
696     if(!alfilt) UNLIKELY
697         context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
698     else try
699     {
700         /* Call the appropriate handler */
701         alfilt->getParamfv(param, values);
702     }
703     catch(filter_exception &e) {
704         context->setError(e.errorCode(), "%s", e.what());
705     }
706 }
707 END_API_FUNC
708
709
710 FilterSubList::~FilterSubList()
711 {
712     uint64_t usemask{~FreeMask};
713     while(usemask)
714     {
715         const int idx{al::countr_zero(usemask)};
716         al::destroy_at(Filters+idx);
717         usemask &= ~(1_u64 << idx);
718     }
719     FreeMask = ~usemask;
720     al_free(Filters);
721     Filters = nullptr;
722 }