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
40 #include "alc/context.h"
41 #include "alc/device.h"
43 #include "alnumeric.h"
44 #include "core/except.h"
45 #include "opthelpers.h"
51 class filter_exception final
: public al::base_exception
{
55 #ifdef __USE_MINGW_ANSI_STDIO
56 [[gnu::format(gnu_printf
, 3, 4)]]
58 [[gnu::format(printf
, 3, 4)]]
60 filter_exception(ALenum code
, const char *msg
, ...);
61 ~filter_exception() override
;
63 ALenum
errorCode() const noexcept
{ return mErrorCode
; }
66 filter_exception::filter_exception(ALenum code
, const char* msg
, ...) : mErrorCode
{code
}
70 setMessage(msg
, args
);
73 filter_exception::~filter_exception() = default;
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, \
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*)
86 throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass integer-vector property 0x%04x",
89 void ALlowpass_setParamf(ALfilter
*filter
, ALenum param
, float val
)
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
};
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
;
106 throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass float property 0x%04x", param
};
109 void ALlowpass_setParamfv(ALfilter
*filter
, ALenum param
, const float *vals
)
110 { ALlowpass_setParamf(filter
, param
, vals
[0]); }
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*)
116 throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass integer-vector property 0x%04x",
119 void ALlowpass_getParamf(const ALfilter
*filter
, ALenum param
, float *val
)
123 case AL_LOWPASS_GAIN
:
127 case AL_LOWPASS_GAINHF
:
128 *val
= filter
->GainHF
;
132 throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass float property 0x%04x", param
};
135 void ALlowpass_getParamfv(const ALfilter
*filter
, ALenum param
, float *vals
)
136 { ALlowpass_getParamf(filter
, param
, vals
); }
138 DEFINE_ALFILTER_VTABLE(ALlowpass
);
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*)
145 throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass integer-vector property 0x%04x",
148 void ALhighpass_setParamf(ALfilter
*filter
, ALenum param
, float val
)
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
};
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
;
165 throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass float property 0x%04x", param
};
168 void ALhighpass_setParamfv(ALfilter
*filter
, ALenum param
, const float *vals
)
169 { ALhighpass_setParamf(filter
, param
, vals
[0]); }
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*)
175 throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass integer-vector property 0x%04x",
178 void ALhighpass_getParamf(const ALfilter
*filter
, ALenum param
, float *val
)
182 case AL_HIGHPASS_GAIN
:
186 case AL_HIGHPASS_GAINLF
:
187 *val
= filter
->GainLF
;
191 throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass float property 0x%04x", param
};
194 void ALhighpass_getParamfv(const ALfilter
*filter
, ALenum param
, float *vals
)
195 { ALhighpass_getParamf(filter
, param
, vals
); }
197 DEFINE_ALFILTER_VTABLE(ALhighpass
);
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*)
204 throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass integer-vector property 0x%04x",
207 void ALbandpass_setParamf(ALfilter
*filter
, ALenum param
, float val
)
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
};
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
;
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
;
230 throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass float property 0x%04x", param
};
233 void ALbandpass_setParamfv(ALfilter
*filter
, ALenum param
, const float *vals
)
234 { ALbandpass_setParamf(filter
, param
, vals
[0]); }
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*)
240 throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass integer-vector property 0x%04x",
243 void ALbandpass_getParamf(const ALfilter
*filter
, ALenum param
, float *val
)
247 case AL_BANDPASS_GAIN
:
251 case AL_BANDPASS_GAINHF
:
252 *val
= filter
->GainHF
;
255 case AL_BANDPASS_GAINLF
:
256 *val
= filter
->GainLF
;
260 throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass float property 0x%04x", param
};
263 void ALbandpass_getParamfv(const ALfilter
*filter
, ALenum param
, float *vals
)
264 { ALbandpass_getParamf(filter
, param
, vals
); }
266 DEFINE_ALFILTER_VTABLE(ALbandpass
);
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
}; }
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
}; }
287 DEFINE_ALFILTER_VTABLE(ALnullfilter
);
290 void InitFilterParams(ALfilter
*filter
, ALenum type
)
292 if(type
== AL_FILTER_LOWPASS
)
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
;
301 else if(type
== AL_FILTER_HIGHPASS
)
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
;
310 else if(type
== AL_FILTER_BANDPASS
)
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
;
322 filter
->GainHF
= 1.0f
;
323 filter
->HFReference
= LOWPASSFREQREF
;
324 filter
->GainLF
= 1.0f
;
325 filter
->LFReference
= HIGHPASSFREQREF
;
326 filter
->vtab
= &ALnullfilter_vtable
;
331 bool EnsureFilters(ALCdevice
*device
, size_t needed
)
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
)); })};
337 while(needed
> count
)
339 if(device
->FilterList
.size() >= 1<<25) UNLIKELY
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
348 device
->FilterList
.pop_back();
357 ALfilter
*AllocFilter(ALCdevice
*device
)
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
));
366 ALfilter
*filter
{al::construct_at(sublist
->Filters
+ slidx
)};
367 InitFilterParams(filter
, AL_FILTER_NULL
);
369 /* Add 1 to avoid filter ID 0. */
370 filter
->id
= ((lidx
<<6) | slidx
) + 1;
372 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
377 void FreeFilter(ALCdevice
*device
, ALfilter
*filter
)
379 const ALuint id
{filter
->id
- 1};
380 const size_t lidx
{id
>> 6};
381 const ALuint slidx
{id
& 0x3f};
383 al::destroy_at(filter
);
385 device
->FilterList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
389 inline ALfilter
*LookupFilter(ALCdevice
*device
, ALuint id
)
391 const size_t lidx
{(id
-1) >> 6};
392 const ALuint slidx
{(id
-1) & 0x3f};
394 if(lidx
>= device
->FilterList
.size()) UNLIKELY
396 FilterSubList
&sublist
= device
->FilterList
[lidx
];
397 if(sublist
.FreeMask
& (1_u64
<< slidx
)) UNLIKELY
399 return sublist
.Filters
+ slidx
;
404 AL_API
void AL_APIENTRY
alGenFilters(ALsizei n
, ALuint
*filters
)
407 ContextRef context
{GetContextRef()};
408 if(!context
) UNLIKELY
return;
411 context
->setError(AL_INVALID_VALUE
, "Generating %d filters", n
);
412 if(n
<= 0) UNLIKELY
return;
414 ALCdevice
*device
{context
->mALDevice
.get()};
415 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
416 if(!EnsureFilters(device
, static_cast<ALuint
>(n
)))
418 context
->setError(AL_OUT_OF_MEMORY
, "Failed to allocate %d filter%s", n
, (n
==1)?"":"s");
424 /* Special handling for the easy and normal case. */
425 ALfilter
*filter
{AllocFilter(device
)};
426 if(filter
) filters
[0] = filter
->id
;
430 /* Store the allocated buffer IDs in a separate local list, to avoid
431 * modifying the user storage in case of failure.
433 al::vector
<ALuint
> ids
;
434 ids
.reserve(static_cast<ALuint
>(n
));
436 ALfilter
*filter
{AllocFilter(device
)};
437 ids
.emplace_back(filter
->id
);
439 std::copy(ids
.begin(), ids
.end(), filters
);
444 AL_API
void AL_APIENTRY
alDeleteFilters(ALsizei n
, const ALuint
*filters
)
447 ContextRef context
{GetContextRef()};
448 if(!context
) UNLIKELY
return;
451 context
->setError(AL_INVALID_VALUE
, "Deleting %d filters", n
);
452 if(n
<= 0) UNLIKELY
return;
454 ALCdevice
*device
{context
->mALDevice
.get()};
455 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
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; };
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
465 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", *invflt
);
469 /* All good. Delete non-0 filter IDs. */
470 auto delete_filter
= [device
](const ALuint fid
) -> void
472 ALfilter
*filter
{fid
? LookupFilter(device
, fid
) : nullptr};
473 if(filter
) FreeFilter(device
, filter
);
475 std::for_each(filters
, filters_end
, delete_filter
);
479 AL_API ALboolean AL_APIENTRY
alIsFilter(ALuint filter
)
482 ContextRef context
{GetContextRef()};
485 ALCdevice
*device
{context
->mALDevice
.get()};
486 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
487 if(!filter
|| LookupFilter(device
, filter
))
495 AL_API
void AL_APIENTRY
alFilteri(ALuint filter
, ALenum param
, ALint value
)
498 ContextRef context
{GetContextRef()};
499 if(!context
) UNLIKELY
return;
501 ALCdevice
*device
{context
->mALDevice
.get()};
502 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
504 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
506 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
509 if(param
== AL_FILTER_TYPE
)
511 if(value
== AL_FILTER_NULL
|| value
== AL_FILTER_LOWPASS
512 || value
== AL_FILTER_HIGHPASS
|| value
== AL_FILTER_BANDPASS
)
513 InitFilterParams(alfilt
, value
);
515 context
->setError(AL_INVALID_VALUE
, "Invalid filter type 0x%04x", value
);
519 /* Call the appropriate handler */
520 alfilt
->setParami(param
, value
);
522 catch(filter_exception
&e
) {
523 context
->setError(e
.errorCode(), "%s", e
.what());
529 AL_API
void AL_APIENTRY
alFilteriv(ALuint filter
, ALenum param
, const ALint
*values
)
535 alFilteri(filter
, param
, values
[0]);
539 ContextRef context
{GetContextRef()};
540 if(!context
) UNLIKELY
return;
542 ALCdevice
*device
{context
->mALDevice
.get()};
543 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
545 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
547 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
550 /* Call the appropriate handler */
551 alfilt
->setParamiv(param
, values
);
553 catch(filter_exception
&e
) {
554 context
->setError(e
.errorCode(), "%s", e
.what());
559 AL_API
void AL_APIENTRY
alFilterf(ALuint filter
, ALenum param
, ALfloat value
)
562 ContextRef context
{GetContextRef()};
563 if(!context
) UNLIKELY
return;
565 ALCdevice
*device
{context
->mALDevice
.get()};
566 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
568 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
570 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
573 /* Call the appropriate handler */
574 alfilt
->setParamf(param
, value
);
576 catch(filter_exception
&e
) {
577 context
->setError(e
.errorCode(), "%s", e
.what());
582 AL_API
void AL_APIENTRY
alFilterfv(ALuint filter
, ALenum param
, const ALfloat
*values
)
585 ContextRef context
{GetContextRef()};
586 if(!context
) UNLIKELY
return;
588 ALCdevice
*device
{context
->mALDevice
.get()};
589 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
591 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
593 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
596 /* Call the appropriate handler */
597 alfilt
->setParamfv(param
, values
);
599 catch(filter_exception
&e
) {
600 context
->setError(e
.errorCode(), "%s", e
.what());
605 AL_API
void AL_APIENTRY
alGetFilteri(ALuint filter
, ALenum param
, ALint
*value
)
608 ContextRef context
{GetContextRef()};
609 if(!context
) UNLIKELY
return;
611 ALCdevice
*device
{context
->mALDevice
.get()};
612 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
614 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
616 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
619 if(param
== AL_FILTER_TYPE
)
620 *value
= alfilt
->type
;
623 /* Call the appropriate handler */
624 alfilt
->getParami(param
, value
);
626 catch(filter_exception
&e
) {
627 context
->setError(e
.errorCode(), "%s", e
.what());
633 AL_API
void AL_APIENTRY
alGetFilteriv(ALuint filter
, ALenum param
, ALint
*values
)
639 alGetFilteri(filter
, param
, values
);
643 ContextRef context
{GetContextRef()};
644 if(!context
) UNLIKELY
return;
646 ALCdevice
*device
{context
->mALDevice
.get()};
647 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
649 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
651 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
654 /* Call the appropriate handler */
655 alfilt
->getParamiv(param
, values
);
657 catch(filter_exception
&e
) {
658 context
->setError(e
.errorCode(), "%s", e
.what());
663 AL_API
void AL_APIENTRY
alGetFilterf(ALuint filter
, ALenum param
, ALfloat
*value
)
666 ContextRef context
{GetContextRef()};
667 if(!context
) UNLIKELY
return;
669 ALCdevice
*device
{context
->mALDevice
.get()};
670 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
672 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
674 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
677 /* Call the appropriate handler */
678 alfilt
->getParamf(param
, value
);
680 catch(filter_exception
&e
) {
681 context
->setError(e
.errorCode(), "%s", e
.what());
686 AL_API
void AL_APIENTRY
alGetFilterfv(ALuint filter
, ALenum param
, ALfloat
*values
)
689 ContextRef context
{GetContextRef()};
690 if(!context
) UNLIKELY
return;
692 ALCdevice
*device
{context
->mALDevice
.get()};
693 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
695 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
697 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
700 /* Call the appropriate handler */
701 alfilt
->getParamfv(param
, values
);
703 catch(filter_exception
&e
) {
704 context
->setError(e
.errorCode(), "%s", e
.what());
710 FilterSubList::~FilterSubList()
712 uint64_t usemask
{~FreeMask
};
715 const int idx
{al::countr_zero(usemask
)};
716 al::destroy_at(Filters
+idx
);
717 usemask
&= ~(1_u64
<< idx
);