Check for unsigned underflow
[openal-soft.git] / al / filter.cpp
blob4ef6b95832016bff5633cadca47a5ede95fd5b3b
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 "filter.h"
25 #include <algorithm>
26 #include <cstdarg>
27 #include <cstdint>
28 #include <cstdio>
29 #include <iterator>
30 #include <memory>
31 #include <mutex>
32 #include <numeric>
33 #include <unordered_map>
34 #include <vector>
36 #include "AL/al.h"
37 #include "AL/alc.h"
38 #include "AL/efx.h"
40 #include "albit.h"
41 #include "alc/context.h"
42 #include "alc/device.h"
43 #include "almalloc.h"
44 #include "alnumeric.h"
45 #include "alspan.h"
46 #include "direct_defs.h"
47 #include "error.h"
48 #include "intrusive_ptr.h"
49 #include "opthelpers.h"
52 namespace {
54 using SubListAllocator = al::allocator<std::array<ALfilter,64>>;
57 void InitFilterParams(ALfilter *filter, ALenum type)
59 if(type == AL_FILTER_LOWPASS)
61 filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
62 filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
63 filter->HFReference = LowPassFreqRef;
64 filter->GainLF = 1.0f;
65 filter->LFReference = HighPassFreqRef;
66 filter->mTypeVariant.emplace<LowpassFilterTable>();
68 else if(type == AL_FILTER_HIGHPASS)
70 filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
71 filter->GainHF = 1.0f;
72 filter->HFReference = LowPassFreqRef;
73 filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
74 filter->LFReference = HighPassFreqRef;
75 filter->mTypeVariant.emplace<HighpassFilterTable>();
77 else if(type == AL_FILTER_BANDPASS)
79 filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
80 filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
81 filter->HFReference = LowPassFreqRef;
82 filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
83 filter->LFReference = HighPassFreqRef;
84 filter->mTypeVariant.emplace<BandpassFilterTable>();
86 else
88 filter->Gain = 1.0f;
89 filter->GainHF = 1.0f;
90 filter->HFReference = LowPassFreqRef;
91 filter->GainLF = 1.0f;
92 filter->LFReference = HighPassFreqRef;
93 filter->mTypeVariant.emplace<NullFilterTable>();
95 filter->type = type;
98 auto EnsureFilters(al::Device *device, size_t needed) noexcept -> bool
99 try {
100 size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), 0_uz,
101 [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
102 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
104 while(needed > count)
106 if(device->FilterList.size() >= 1<<25) UNLIKELY
107 return false;
109 FilterSubList sublist{};
110 sublist.FreeMask = ~0_u64;
111 sublist.Filters = SubListAllocator{}.allocate(1);
112 device->FilterList.emplace_back(std::move(sublist));
113 count += std::tuple_size_v<SubListAllocator::value_type>;
115 return true;
117 catch(...) {
118 return false;
122 auto AllocFilter(al::Device *device) noexcept -> ALfilter*
124 auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
125 [](const FilterSubList &entry) noexcept -> bool
126 { return entry.FreeMask != 0; });
127 auto lidx = static_cast<ALuint>(std::distance(device->FilterList.begin(), sublist));
128 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
129 ASSUME(slidx < 64);
131 ALfilter *filter{al::construct_at(al::to_address(sublist->Filters->begin() + slidx))};
132 InitFilterParams(filter, AL_FILTER_NULL);
134 /* Add 1 to avoid filter ID 0. */
135 filter->id = ((lidx<<6) | slidx) + 1;
137 sublist->FreeMask &= ~(1_u64 << slidx);
139 return filter;
142 void FreeFilter(al::Device *device, ALfilter *filter)
144 device->mFilterNames.erase(filter->id);
146 const ALuint id{filter->id - 1};
147 const size_t lidx{id >> 6};
148 const ALuint slidx{id & 0x3f};
150 std::destroy_at(filter);
152 device->FilterList[lidx].FreeMask |= 1_u64 << slidx;
156 auto LookupFilter(al::Device *device, ALuint id) noexcept -> ALfilter*
158 const size_t lidx{(id-1) >> 6};
159 const ALuint slidx{(id-1) & 0x3f};
161 if(lidx >= device->FilterList.size()) UNLIKELY
162 return nullptr;
163 FilterSubList &sublist = device->FilterList[lidx];
164 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
165 return nullptr;
166 return al::to_address(sublist.Filters->begin() + slidx);
169 } // namespace
171 /* Null filter parameter handlers */
172 template<>
173 void FilterTable<NullFilterTable>::setParami(ALfilter*, ALenum param, int)
174 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
175 template<>
176 void FilterTable<NullFilterTable>::setParamiv(ALfilter*, ALenum param, const int*)
177 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
178 template<>
179 void FilterTable<NullFilterTable>::setParamf(ALfilter*, ALenum param, float)
180 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
181 template<>
182 void FilterTable<NullFilterTable>::setParamfv(ALfilter*, ALenum param, const float*)
183 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
184 template<>
185 void FilterTable<NullFilterTable>::getParami(const ALfilter*, ALenum param, int*)
186 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
187 template<>
188 void FilterTable<NullFilterTable>::getParamiv(const ALfilter*, ALenum param, int*)
189 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
190 template<>
191 void FilterTable<NullFilterTable>::getParamf(const ALfilter*, ALenum param, float*)
192 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
193 template<>
194 void FilterTable<NullFilterTable>::getParamfv(const ALfilter*, ALenum param, float*)
195 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
197 /* Lowpass parameter handlers */
198 template<>
199 void FilterTable<LowpassFilterTable>::setParami(ALfilter*, ALenum param, int)
200 { throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
201 template<>
202 void FilterTable<LowpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
203 { setParami(filter, param, *values); }
204 template<>
205 void FilterTable<LowpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
207 switch(param)
209 case AL_LOWPASS_GAIN:
210 if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
211 throw al::context_error{AL_INVALID_VALUE, "Low-pass gain %f out of range", val};
212 filter->Gain = val;
213 return;
215 case AL_LOWPASS_GAINHF:
216 if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
217 throw al::context_error{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val};
218 filter->GainHF = val;
219 return;
221 throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
223 template<>
224 void FilterTable<LowpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
225 { setParamf(filter, param, *vals); }
226 template<>
227 void FilterTable<LowpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
228 { throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
229 template<>
230 void FilterTable<LowpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
231 { getParami(filter, param, values); }
232 template<>
233 void FilterTable<LowpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
235 switch(param)
237 case AL_LOWPASS_GAIN: *val = filter->Gain; return;
238 case AL_LOWPASS_GAINHF: *val = filter->GainHF; return;
240 throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
242 template<>
243 void FilterTable<LowpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
244 { getParamf(filter, param, vals); }
246 /* Highpass parameter handlers */
247 template<>
248 void FilterTable<HighpassFilterTable>::setParami(ALfilter*, ALenum param, int)
249 { throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
250 template<>
251 void FilterTable<HighpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
252 { setParami(filter, param, *values); }
253 template<>
254 void FilterTable<HighpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
256 switch(param)
258 case AL_HIGHPASS_GAIN:
259 if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
260 throw al::context_error{AL_INVALID_VALUE, "High-pass gain %f out of range", val};
261 filter->Gain = val;
262 return;
264 case AL_HIGHPASS_GAINLF:
265 if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
266 throw al::context_error{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val};
267 filter->GainLF = val;
268 return;
270 throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
272 template<>
273 void FilterTable<HighpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
274 { setParamf(filter, param, *vals); }
275 template<>
276 void FilterTable<HighpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
277 { throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
278 template<>
279 void FilterTable<HighpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
280 { getParami(filter, param, values); }
281 template<>
282 void FilterTable<HighpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
284 switch(param)
286 case AL_HIGHPASS_GAIN: *val = filter->Gain; return;
287 case AL_HIGHPASS_GAINLF: *val = filter->GainLF; return;
289 throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
291 template<>
292 void FilterTable<HighpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
293 { getParamf(filter, param, vals); }
295 /* Bandpass parameter handlers */
296 template<>
297 void FilterTable<BandpassFilterTable>::setParami(ALfilter*, ALenum param, int)
298 { throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
299 template<>
300 void FilterTable<BandpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
301 { setParami(filter, param, *values); }
302 template<>
303 void FilterTable<BandpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
305 switch(param)
307 case AL_BANDPASS_GAIN:
308 if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
309 throw al::context_error{AL_INVALID_VALUE, "Band-pass gain %f out of range", val};
310 filter->Gain = val;
311 return;
313 case AL_BANDPASS_GAINHF:
314 if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
315 throw al::context_error{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val};
316 filter->GainHF = val;
317 return;
319 case AL_BANDPASS_GAINLF:
320 if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
321 throw al::context_error{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val};
322 filter->GainLF = val;
323 return;
325 throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
327 template<>
328 void FilterTable<BandpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
329 { setParamf(filter, param, *vals); }
330 template<>
331 void FilterTable<BandpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
332 { throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
333 template<>
334 void FilterTable<BandpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
335 { getParami(filter, param, values); }
336 template<>
337 void FilterTable<BandpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
339 switch(param)
341 case AL_BANDPASS_GAIN: *val = filter->Gain; return;
342 case AL_BANDPASS_GAINHF: *val = filter->GainHF; return;
343 case AL_BANDPASS_GAINLF: *val = filter->GainLF; return;
345 throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
347 template<>
348 void FilterTable<BandpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
349 { getParamf(filter, param, vals); }
352 AL_API DECL_FUNC2(void, alGenFilters, ALsizei,n, ALuint*,filters)
353 FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept
354 try {
355 if(n < 0)
356 throw al::context_error{AL_INVALID_VALUE, "Generating %d filters", n};
357 if(n <= 0) UNLIKELY return;
359 auto *device = context->mALDevice.get();
360 auto filterlock = std::lock_guard{device->FilterLock};
362 const al::span fids{filters, static_cast<ALuint>(n)};
363 if(!EnsureFilters(device, fids.size()))
364 throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n,
365 (n == 1) ? "" : "s"};
367 std::generate(fids.begin(), fids.end(), [device]{ return AllocFilter(device)->id; });
369 catch(al::context_error& e) {
370 context->setError(e.errorCode(), "%s", e.what());
373 AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei,n, const ALuint*,filters)
374 FORCE_ALIGN void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n,
375 const ALuint *filters) noexcept
376 try {
377 if(n < 0)
378 throw al::context_error{AL_INVALID_VALUE, "Deleting %d filters", n};
379 if(n <= 0) UNLIKELY return;
381 auto *device = context->mALDevice.get();
382 auto filterlock = std::lock_guard{device->FilterLock};
384 /* First try to find any filters that are invalid. */
385 auto validate_filter = [device](const ALuint fid) -> bool
386 { return !fid || LookupFilter(device, fid) != nullptr; };
388 const al::span fids{filters, static_cast<ALuint>(n)};
389 auto invflt = std::find_if_not(fids.begin(), fids.end(), validate_filter);
390 if(invflt != fids.end())
391 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", *invflt};
393 /* All good. Delete non-0 filter IDs. */
394 auto delete_filter = [device](const ALuint fid) -> void
396 if(ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr})
397 FreeFilter(device, filter);
399 std::for_each(fids.begin(), fids.end(), delete_filter);
401 catch(al::context_error& e) {
402 context->setError(e.errorCode(), "%s", e.what());
405 AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint,filter)
406 FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept
408 auto *device = context->mALDevice.get();
409 auto filterlock = std::lock_guard{device->FilterLock};
410 if(!filter || LookupFilter(device, filter))
411 return AL_TRUE;
412 return AL_FALSE;
416 AL_API DECL_FUNC3(void, alFilteri, ALuint,filter, ALenum,param, ALint,value)
417 FORCE_ALIGN void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
418 ALint value) noexcept
419 try {
420 auto *device = context->mALDevice.get();
421 auto filterlock = std::lock_guard{device->FilterLock};
423 ALfilter *alfilt{LookupFilter(device, filter)};
424 if(!alfilt)
425 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
427 switch(param)
429 case AL_FILTER_TYPE:
430 if(!(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
431 || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS))
432 throw al::context_error{AL_INVALID_VALUE, "Invalid filter type 0x%04x", value};
433 InitFilterParams(alfilt, value);
434 return;
437 /* Call the appropriate handler */
438 std::visit([alfilt,param,value](auto&& thunk){thunk.setParami(alfilt, param, value);},
439 alfilt->mTypeVariant);
441 catch(al::context_error& e) {
442 context->setError(e.errorCode(), "%s", e.what());
445 AL_API DECL_FUNC3(void, alFilteriv, ALuint,filter, ALenum,param, const ALint*,values)
446 FORCE_ALIGN void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param,
447 const ALint *values) noexcept
448 try {
449 switch(param)
451 case AL_FILTER_TYPE:
452 alFilteriDirect(context, filter, param, *values);
453 return;
456 auto *device = context->mALDevice.get();
457 auto filterlock = std::lock_guard{device->FilterLock};
459 ALfilter *alfilt{LookupFilter(device, filter)};
460 if(!alfilt)
461 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
463 /* Call the appropriate handler */
464 std::visit([alfilt,param,values](auto&& thunk){thunk.setParamiv(alfilt, param, values);},
465 alfilt->mTypeVariant);
467 catch(al::context_error& e) {
468 context->setError(e.errorCode(), "%s", e.what());
471 AL_API DECL_FUNC3(void, alFilterf, ALuint,filter, ALenum,param, ALfloat,value)
472 FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
473 ALfloat value) noexcept
474 try {
475 auto *device = context->mALDevice.get();
476 auto filterlock = std::lock_guard{device->FilterLock};
478 ALfilter *alfilt{LookupFilter(device, filter)};
479 if(!alfilt)
480 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
482 /* Call the appropriate handler */
483 std::visit([alfilt,param,value](auto&& thunk){thunk.setParamf(alfilt, param, value);},
484 alfilt->mTypeVariant);
486 catch(al::context_error& e) {
487 context->setError(e.errorCode(), "%s", e.what());
490 AL_API DECL_FUNC3(void, alFilterfv, ALuint,filter, ALenum,param, const ALfloat*,values)
491 FORCE_ALIGN void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
492 const ALfloat *values) noexcept
493 try {
494 auto *device = context->mALDevice.get();
495 auto filterlock = std::lock_guard{device->FilterLock};
497 ALfilter *alfilt{LookupFilter(device, filter)};
498 if(!alfilt)
499 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
501 /* Call the appropriate handler */
502 std::visit([alfilt,param,values](auto&& thunk){thunk.setParamfv(alfilt, param, values);},
503 alfilt->mTypeVariant);
505 catch(al::context_error& e) {
506 context->setError(e.errorCode(), "%s", e.what());
509 AL_API DECL_FUNC3(void, alGetFilteri, ALuint,filter, ALenum,param, ALint*,value)
510 FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
511 ALint *value) noexcept
512 try {
513 auto *device = context->mALDevice.get();
514 auto filterlock = std::lock_guard{device->FilterLock};
516 const ALfilter *alfilt{LookupFilter(device, filter)};
517 if(!alfilt)
518 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
520 switch(param)
522 case AL_FILTER_TYPE:
523 *value = alfilt->type;
524 return;
527 /* Call the appropriate handler */
528 std::visit([alfilt,param,value](auto&& thunk){thunk.getParami(alfilt, param, value);},
529 alfilt->mTypeVariant);
531 catch(al::context_error& e) {
532 context->setError(e.errorCode(), "%s", e.what());
535 AL_API DECL_FUNC3(void, alGetFilteriv, ALuint,filter, ALenum,param, ALint*,values)
536 FORCE_ALIGN void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param,
537 ALint *values) noexcept
538 try {
539 switch(param)
541 case AL_FILTER_TYPE:
542 alGetFilteriDirect(context, filter, param, values);
543 return;
546 auto *device = context->mALDevice.get();
547 auto filterlock = std::lock_guard{device->FilterLock};
549 const ALfilter *alfilt{LookupFilter(device, filter)};
550 if(!alfilt)
551 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
553 /* Call the appropriate handler */
554 std::visit([alfilt,param,values](auto&& thunk){thunk.getParamiv(alfilt, param, values);},
555 alfilt->mTypeVariant);
557 catch(al::context_error& e) {
558 context->setError(e.errorCode(), "%s", e.what());
561 AL_API DECL_FUNC3(void, alGetFilterf, ALuint,filter, ALenum,param, ALfloat*,value)
562 FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
563 ALfloat *value) noexcept
564 try {
565 auto *device = context->mALDevice.get();
566 auto filterlock = std::lock_guard{device->FilterLock};
568 const ALfilter *alfilt{LookupFilter(device, filter)};
569 if(!alfilt) UNLIKELY
570 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
572 /* Call the appropriate handler */
573 std::visit([alfilt,param,value](auto&& thunk){thunk.getParamf(alfilt, param, value);},
574 alfilt->mTypeVariant);
576 catch(al::context_error& e) {
577 context->setError(e.errorCode(), "%s", e.what());
580 AL_API DECL_FUNC3(void, alGetFilterfv, ALuint,filter, ALenum,param, ALfloat*,values)
581 FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
582 ALfloat *values) noexcept
583 try {
584 auto *device = context->mALDevice.get();
585 auto filterlock = std::lock_guard{device->FilterLock};
587 const ALfilter *alfilt{LookupFilter(device, filter)};
588 if(!alfilt) UNLIKELY
589 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
591 /* Call the appropriate handler */
592 std::visit([alfilt,param,values](auto&& thunk){thunk.getParamfv(alfilt, param, values);},
593 alfilt->mTypeVariant);
595 catch(al::context_error& e) {
596 context->setError(e.errorCode(), "%s", e.what());
600 void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name)
602 auto *device = context->mALDevice.get();
603 auto filterlock = std::lock_guard{device->FilterLock};
605 auto filter = LookupFilter(device, id);
606 if(!filter)
607 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", id};
609 device->mFilterNames.insert_or_assign(id, name);
613 FilterSubList::~FilterSubList()
615 if(!Filters)
616 return;
618 uint64_t usemask{~FreeMask};
619 while(usemask)
621 const int idx{al::countr_zero(usemask)};
622 std::destroy_at(al::to_address(Filters->begin() + idx));
623 usemask &= ~(1_u64 << idx);
625 FreeMask = ~usemask;
626 SubListAllocator{}.deallocate(Filters, 1);
627 Filters = nullptr;