Release 1.24.0
[openal-soft.git] / al / filter.cpp
blob5e56c6f047492caf836d2e644ff87a7eaa47f24f
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 <string>
34 #include <unordered_map>
35 #include <vector>
37 #include "AL/al.h"
38 #include "AL/alc.h"
39 #include "AL/efx.h"
41 #include "albit.h"
42 #include "alc/context.h"
43 #include "alc/device.h"
44 #include "alc/inprogext.h"
45 #include "almalloc.h"
46 #include "alnumeric.h"
47 #include "alspan.h"
48 #include "direct_defs.h"
49 #include "error.h"
50 #include "intrusive_ptr.h"
51 #include "opthelpers.h"
54 namespace {
56 using SubListAllocator = al::allocator<std::array<ALfilter,64>>;
59 void InitFilterParams(ALfilter *filter, ALenum type)
61 if(type == AL_FILTER_LOWPASS)
63 filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
64 filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
65 filter->HFReference = LowPassFreqRef;
66 filter->GainLF = 1.0f;
67 filter->LFReference = HighPassFreqRef;
68 filter->mTypeVariant.emplace<LowpassFilterTable>();
70 else if(type == AL_FILTER_HIGHPASS)
72 filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
73 filter->GainHF = 1.0f;
74 filter->HFReference = LowPassFreqRef;
75 filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
76 filter->LFReference = HighPassFreqRef;
77 filter->mTypeVariant.emplace<HighpassFilterTable>();
79 else if(type == AL_FILTER_BANDPASS)
81 filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
82 filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
83 filter->HFReference = LowPassFreqRef;
84 filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
85 filter->LFReference = HighPassFreqRef;
86 filter->mTypeVariant.emplace<BandpassFilterTable>();
88 else
90 filter->Gain = 1.0f;
91 filter->GainHF = 1.0f;
92 filter->HFReference = LowPassFreqRef;
93 filter->GainLF = 1.0f;
94 filter->LFReference = HighPassFreqRef;
95 filter->mTypeVariant.emplace<NullFilterTable>();
97 filter->type = type;
100 auto EnsureFilters(ALCdevice *device, size_t needed) noexcept -> bool
101 try {
102 size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), 0_uz,
103 [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
104 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
106 while(needed > count)
108 if(device->FilterList.size() >= 1<<25) UNLIKELY
109 return false;
111 FilterSubList sublist{};
112 sublist.FreeMask = ~0_u64;
113 sublist.Filters = SubListAllocator{}.allocate(1);
114 device->FilterList.emplace_back(std::move(sublist));
115 count += std::tuple_size_v<SubListAllocator::value_type>;
117 return true;
119 catch(...) {
120 return false;
124 ALfilter *AllocFilter(ALCdevice *device) noexcept
126 auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
127 [](const FilterSubList &entry) noexcept -> bool
128 { return entry.FreeMask != 0; });
129 auto lidx = static_cast<ALuint>(std::distance(device->FilterList.begin(), sublist));
130 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
131 ASSUME(slidx < 64);
133 ALfilter *filter{al::construct_at(al::to_address(sublist->Filters->begin() + slidx))};
134 InitFilterParams(filter, AL_FILTER_NULL);
136 /* Add 1 to avoid filter ID 0. */
137 filter->id = ((lidx<<6) | slidx) + 1;
139 sublist->FreeMask &= ~(1_u64 << slidx);
141 return filter;
144 void FreeFilter(ALCdevice *device, ALfilter *filter)
146 device->mFilterNames.erase(filter->id);
148 const ALuint id{filter->id - 1};
149 const size_t lidx{id >> 6};
150 const ALuint slidx{id & 0x3f};
152 std::destroy_at(filter);
154 device->FilterList[lidx].FreeMask |= 1_u64 << slidx;
158 auto LookupFilter(ALCdevice *device, ALuint id) noexcept -> ALfilter*
160 const size_t lidx{(id-1) >> 6};
161 const ALuint slidx{(id-1) & 0x3f};
163 if(lidx >= device->FilterList.size()) UNLIKELY
164 return nullptr;
165 FilterSubList &sublist = device->FilterList[lidx];
166 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
167 return nullptr;
168 return al::to_address(sublist.Filters->begin() + slidx);
171 } // namespace
173 /* Null filter parameter handlers */
174 template<>
175 void FilterTable<NullFilterTable>::setParami(ALfilter*, ALenum param, int)
176 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
177 template<>
178 void FilterTable<NullFilterTable>::setParamiv(ALfilter*, ALenum param, const int*)
179 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
180 template<>
181 void FilterTable<NullFilterTable>::setParamf(ALfilter*, ALenum param, float)
182 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
183 template<>
184 void FilterTable<NullFilterTable>::setParamfv(ALfilter*, ALenum param, const float*)
185 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
186 template<>
187 void FilterTable<NullFilterTable>::getParami(const ALfilter*, ALenum param, int*)
188 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
189 template<>
190 void FilterTable<NullFilterTable>::getParamiv(const ALfilter*, ALenum param, int*)
191 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
192 template<>
193 void FilterTable<NullFilterTable>::getParamf(const ALfilter*, ALenum param, float*)
194 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
195 template<>
196 void FilterTable<NullFilterTable>::getParamfv(const ALfilter*, ALenum param, float*)
197 { throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
199 /* Lowpass parameter handlers */
200 template<>
201 void FilterTable<LowpassFilterTable>::setParami(ALfilter*, ALenum param, int)
202 { throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
203 template<>
204 void FilterTable<LowpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
205 { setParami(filter, param, *values); }
206 template<>
207 void FilterTable<LowpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
209 switch(param)
211 case AL_LOWPASS_GAIN:
212 if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
213 throw al::context_error{AL_INVALID_VALUE, "Low-pass gain %f out of range", val};
214 filter->Gain = val;
215 return;
217 case AL_LOWPASS_GAINHF:
218 if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
219 throw al::context_error{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val};
220 filter->GainHF = val;
221 return;
223 throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
225 template<>
226 void FilterTable<LowpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
227 { setParamf(filter, param, *vals); }
228 template<>
229 void FilterTable<LowpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
230 { throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
231 template<>
232 void FilterTable<LowpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
233 { getParami(filter, param, values); }
234 template<>
235 void FilterTable<LowpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
237 switch(param)
239 case AL_LOWPASS_GAIN: *val = filter->Gain; return;
240 case AL_LOWPASS_GAINHF: *val = filter->GainHF; return;
242 throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
244 template<>
245 void FilterTable<LowpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
246 { getParamf(filter, param, vals); }
248 /* Highpass parameter handlers */
249 template<>
250 void FilterTable<HighpassFilterTable>::setParami(ALfilter*, ALenum param, int)
251 { throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
252 template<>
253 void FilterTable<HighpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
254 { setParami(filter, param, *values); }
255 template<>
256 void FilterTable<HighpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
258 switch(param)
260 case AL_HIGHPASS_GAIN:
261 if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
262 throw al::context_error{AL_INVALID_VALUE, "High-pass gain %f out of range", val};
263 filter->Gain = val;
264 return;
266 case AL_HIGHPASS_GAINLF:
267 if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
268 throw al::context_error{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val};
269 filter->GainLF = val;
270 return;
272 throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
274 template<>
275 void FilterTable<HighpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
276 { setParamf(filter, param, *vals); }
277 template<>
278 void FilterTable<HighpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
279 { throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
280 template<>
281 void FilterTable<HighpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
282 { getParami(filter, param, values); }
283 template<>
284 void FilterTable<HighpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
286 switch(param)
288 case AL_HIGHPASS_GAIN: *val = filter->Gain; return;
289 case AL_HIGHPASS_GAINLF: *val = filter->GainLF; return;
291 throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
293 template<>
294 void FilterTable<HighpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
295 { getParamf(filter, param, vals); }
297 /* Bandpass parameter handlers */
298 template<>
299 void FilterTable<BandpassFilterTable>::setParami(ALfilter*, ALenum param, int)
300 { throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
301 template<>
302 void FilterTable<BandpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
303 { setParami(filter, param, *values); }
304 template<>
305 void FilterTable<BandpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
307 switch(param)
309 case AL_BANDPASS_GAIN:
310 if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
311 throw al::context_error{AL_INVALID_VALUE, "Band-pass gain %f out of range", val};
312 filter->Gain = val;
313 return;
315 case AL_BANDPASS_GAINHF:
316 if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
317 throw al::context_error{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val};
318 filter->GainHF = val;
319 return;
321 case AL_BANDPASS_GAINLF:
322 if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
323 throw al::context_error{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val};
324 filter->GainLF = val;
325 return;
327 throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
329 template<>
330 void FilterTable<BandpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
331 { setParamf(filter, param, *vals); }
332 template<>
333 void FilterTable<BandpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
334 { throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
335 template<>
336 void FilterTable<BandpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
337 { getParami(filter, param, values); }
338 template<>
339 void FilterTable<BandpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
341 switch(param)
343 case AL_BANDPASS_GAIN: *val = filter->Gain; return;
344 case AL_BANDPASS_GAINHF: *val = filter->GainHF; return;
345 case AL_BANDPASS_GAINLF: *val = filter->GainLF; return;
347 throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
349 template<>
350 void FilterTable<BandpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
351 { getParamf(filter, param, vals); }
354 AL_API DECL_FUNC2(void, alGenFilters, ALsizei,n, ALuint*,filters)
355 FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept
356 try {
357 if(n < 0)
358 throw al::context_error{AL_INVALID_VALUE, "Generating %d filters", n};
359 if(n <= 0) UNLIKELY return;
361 ALCdevice *device{context->mALDevice.get()};
362 std::lock_guard<std::mutex> filterlock{device->FilterLock};
364 const al::span fids{filters, static_cast<ALuint>(n)};
365 if(!EnsureFilters(device, fids.size()))
366 throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n,
367 (n == 1) ? "" : "s"};
369 std::generate(fids.begin(), fids.end(), [device]{ return AllocFilter(device)->id; });
371 catch(al::context_error& e) {
372 context->setError(e.errorCode(), "%s", e.what());
375 AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei,n, const ALuint*,filters)
376 FORCE_ALIGN void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n,
377 const ALuint *filters) noexcept
378 try {
379 if(n < 0)
380 throw al::context_error{AL_INVALID_VALUE, "Deleting %d filters", n};
381 if(n <= 0) UNLIKELY return;
383 ALCdevice *device{context->mALDevice.get()};
384 std::lock_guard<std::mutex> filterlock{device->FilterLock};
386 /* First try to find any filters that are invalid. */
387 auto validate_filter = [device](const ALuint fid) -> bool
388 { return !fid || LookupFilter(device, fid) != nullptr; };
390 const al::span fids{filters, static_cast<ALuint>(n)};
391 auto invflt = std::find_if_not(fids.begin(), fids.end(), validate_filter);
392 if(invflt != fids.end())
393 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", *invflt};
395 /* All good. Delete non-0 filter IDs. */
396 auto delete_filter = [device](const ALuint fid) -> void
398 if(ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr})
399 FreeFilter(device, filter);
401 std::for_each(fids.begin(), fids.end(), delete_filter);
403 catch(al::context_error& e) {
404 context->setError(e.errorCode(), "%s", e.what());
407 AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint,filter)
408 FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept
410 ALCdevice *device{context->mALDevice.get()};
411 std::lock_guard<std::mutex> filterlock{device->FilterLock};
412 if(!filter || LookupFilter(device, filter))
413 return AL_TRUE;
414 return AL_FALSE;
418 AL_API DECL_FUNC3(void, alFilteri, ALuint,filter, ALenum,param, ALint,value)
419 FORCE_ALIGN void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
420 ALint value) noexcept
421 try {
422 ALCdevice *device{context->mALDevice.get()};
423 std::lock_guard<std::mutex> filterlock{device->FilterLock};
425 ALfilter *alfilt{LookupFilter(device, filter)};
426 if(!alfilt)
427 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
429 switch(param)
431 case AL_FILTER_TYPE:
432 if(!(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
433 || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS))
434 throw al::context_error{AL_INVALID_VALUE, "Invalid filter type 0x%04x", value};
435 InitFilterParams(alfilt, value);
436 return;
439 /* Call the appropriate handler */
440 std::visit([alfilt,param,value](auto&& thunk){thunk.setParami(alfilt, param, value);},
441 alfilt->mTypeVariant);
443 catch(al::context_error& e) {
444 context->setError(e.errorCode(), "%s", e.what());
447 AL_API DECL_FUNC3(void, alFilteriv, ALuint,filter, ALenum,param, const ALint*,values)
448 FORCE_ALIGN void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param,
449 const ALint *values) noexcept
450 try {
451 switch(param)
453 case AL_FILTER_TYPE:
454 alFilteriDirect(context, filter, param, *values);
455 return;
458 ALCdevice *device{context->mALDevice.get()};
459 std::lock_guard<std::mutex> filterlock{device->FilterLock};
461 ALfilter *alfilt{LookupFilter(device, filter)};
462 if(!alfilt)
463 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
465 /* Call the appropriate handler */
466 std::visit([alfilt,param,values](auto&& thunk){thunk.setParamiv(alfilt, param, values);},
467 alfilt->mTypeVariant);
469 catch(al::context_error& e) {
470 context->setError(e.errorCode(), "%s", e.what());
473 AL_API DECL_FUNC3(void, alFilterf, ALuint,filter, ALenum,param, ALfloat,value)
474 FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
475 ALfloat value) noexcept
476 try {
477 ALCdevice *device{context->mALDevice.get()};
478 std::lock_guard<std::mutex> filterlock{device->FilterLock};
480 ALfilter *alfilt{LookupFilter(device, filter)};
481 if(!alfilt)
482 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
484 /* Call the appropriate handler */
485 std::visit([alfilt,param,value](auto&& thunk){thunk.setParamf(alfilt, param, value);},
486 alfilt->mTypeVariant);
488 catch(al::context_error& e) {
489 context->setError(e.errorCode(), "%s", e.what());
492 AL_API DECL_FUNC3(void, alFilterfv, ALuint,filter, ALenum,param, const ALfloat*,values)
493 FORCE_ALIGN void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
494 const ALfloat *values) noexcept
495 try {
496 ALCdevice *device{context->mALDevice.get()};
497 std::lock_guard<std::mutex> filterlock{device->FilterLock};
499 ALfilter *alfilt{LookupFilter(device, filter)};
500 if(!alfilt)
501 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
503 /* Call the appropriate handler */
504 std::visit([alfilt,param,values](auto&& thunk){thunk.setParamfv(alfilt, param, values);},
505 alfilt->mTypeVariant);
507 catch(al::context_error& e) {
508 context->setError(e.errorCode(), "%s", e.what());
511 AL_API DECL_FUNC3(void, alGetFilteri, ALuint,filter, ALenum,param, ALint*,value)
512 FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
513 ALint *value) noexcept
514 try {
515 ALCdevice *device{context->mALDevice.get()};
516 std::lock_guard<std::mutex> filterlock{device->FilterLock};
518 const ALfilter *alfilt{LookupFilter(device, filter)};
519 if(!alfilt)
520 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
522 switch(param)
524 case AL_FILTER_TYPE:
525 *value = alfilt->type;
526 return;
529 /* Call the appropriate handler */
530 std::visit([alfilt,param,value](auto&& thunk){thunk.getParami(alfilt, param, value);},
531 alfilt->mTypeVariant);
533 catch(al::context_error& e) {
534 context->setError(e.errorCode(), "%s", e.what());
537 AL_API DECL_FUNC3(void, alGetFilteriv, ALuint,filter, ALenum,param, ALint*,values)
538 FORCE_ALIGN void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param,
539 ALint *values) noexcept
540 try {
541 switch(param)
543 case AL_FILTER_TYPE:
544 alGetFilteriDirect(context, filter, param, values);
545 return;
548 ALCdevice *device{context->mALDevice.get()};
549 std::lock_guard<std::mutex> filterlock{device->FilterLock};
551 const ALfilter *alfilt{LookupFilter(device, filter)};
552 if(!alfilt)
553 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
555 /* Call the appropriate handler */
556 std::visit([alfilt,param,values](auto&& thunk){thunk.getParamiv(alfilt, param, values);},
557 alfilt->mTypeVariant);
559 catch(al::context_error& e) {
560 context->setError(e.errorCode(), "%s", e.what());
563 AL_API DECL_FUNC3(void, alGetFilterf, ALuint,filter, ALenum,param, ALfloat*,value)
564 FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
565 ALfloat *value) noexcept
566 try {
567 ALCdevice *device{context->mALDevice.get()};
568 std::lock_guard<std::mutex> filterlock{device->FilterLock};
570 const ALfilter *alfilt{LookupFilter(device, filter)};
571 if(!alfilt) UNLIKELY
572 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
574 /* Call the appropriate handler */
575 std::visit([alfilt,param,value](auto&& thunk){thunk.getParamf(alfilt, param, value);},
576 alfilt->mTypeVariant);
578 catch(al::context_error& e) {
579 context->setError(e.errorCode(), "%s", e.what());
582 AL_API DECL_FUNC3(void, alGetFilterfv, ALuint,filter, ALenum,param, ALfloat*,values)
583 FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
584 ALfloat *values) noexcept
585 try {
586 ALCdevice *device{context->mALDevice.get()};
587 std::lock_guard<std::mutex> filterlock{device->FilterLock};
589 const ALfilter *alfilt{LookupFilter(device, filter)};
590 if(!alfilt) UNLIKELY
591 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
593 /* Call the appropriate handler */
594 std::visit([alfilt,param,values](auto&& thunk){thunk.getParamfv(alfilt, param, values);},
595 alfilt->mTypeVariant);
597 catch(al::context_error& e) {
598 context->setError(e.errorCode(), "%s", e.what());
602 void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name)
604 ALCdevice *device{context->mALDevice.get()};
605 std::lock_guard<std::mutex> filterlock{device->FilterLock};
607 auto filter = LookupFilter(device, id);
608 if(!filter)
609 throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", id};
611 device->mFilterNames.insert_or_assign(id, name);
615 FilterSubList::~FilterSubList()
617 if(!Filters)
618 return;
620 uint64_t usemask{~FreeMask};
621 while(usemask)
623 const int idx{al::countr_zero(usemask)};
624 std::destroy_at(al::to_address(Filters->begin() + idx));
625 usemask &= ~(1_u64 << idx);
627 FreeMask = ~usemask;
628 SubListAllocator{}.deallocate(Filters, 1);
629 Filters = nullptr;