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 "alcontext.h"
43 #include "alnumeric.h"
44 #include "opthelpers.h"
50 class filter_exception final
: public al::base_exception
{
52 [[gnu::format(printf
, 3, 4)]]
53 filter_exception(ALenum code
, const char *msg
, ...) : base_exception
{code
}
57 setMessage(msg
, args
);
62 #define FILTER_MIN_GAIN 0.0f
63 #define FILTER_MAX_GAIN 4.0f /* +12dB */
65 #define DEFINE_ALFILTER_VTABLE(T) \
66 const ALfilter::Vtable T##_vtable = { \
67 T##_setParami, T##_setParamiv, T##_setParamf, T##_setParamfv, \
68 T##_getParami, T##_getParamiv, T##_getParamf, T##_getParamfv, \
71 void ALlowpass_setParami(ALfilter
*, ALenum param
, int)
72 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass integer property 0x%04x", param
}; }
73 void ALlowpass_setParamiv(ALfilter
*, ALenum param
, const int*)
75 throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass integer-vector property 0x%04x",
78 void ALlowpass_setParamf(ALfilter
*filter
, ALenum param
, float val
)
83 if(!(val
>= FILTER_MIN_GAIN
&& val
<= FILTER_MAX_GAIN
))
84 throw filter_exception
{AL_INVALID_VALUE
, "Low-pass gain %f out of range", val
};
88 case AL_LOWPASS_GAINHF
:
89 if(!(val
>= AL_LOWPASS_MIN_GAINHF
&& val
<= AL_LOWPASS_MAX_GAINHF
))
90 throw filter_exception
{AL_INVALID_VALUE
, "Low-pass gainhf %f out of range", val
};
95 throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass float property 0x%04x", param
};
98 void ALlowpass_setParamfv(ALfilter
*filter
, ALenum param
, const float *vals
)
99 { ALlowpass_setParamf(filter
, param
, vals
[0]); }
101 void ALlowpass_getParami(const ALfilter
*, ALenum param
, int*)
102 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass integer property 0x%04x", param
}; }
103 void ALlowpass_getParamiv(const ALfilter
*, ALenum param
, int*)
105 throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass integer-vector property 0x%04x",
108 void ALlowpass_getParamf(const ALfilter
*filter
, ALenum param
, float *val
)
112 case AL_LOWPASS_GAIN
:
116 case AL_LOWPASS_GAINHF
:
117 *val
= filter
->GainHF
;
121 throw filter_exception
{AL_INVALID_ENUM
, "Invalid low-pass float property 0x%04x", param
};
124 void ALlowpass_getParamfv(const ALfilter
*filter
, ALenum param
, float *vals
)
125 { ALlowpass_getParamf(filter
, param
, vals
); }
127 DEFINE_ALFILTER_VTABLE(ALlowpass
);
130 void ALhighpass_setParami(ALfilter
*, ALenum param
, int)
131 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass integer property 0x%04x", param
}; }
132 void ALhighpass_setParamiv(ALfilter
*, ALenum param
, const int*)
134 throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass integer-vector property 0x%04x",
137 void ALhighpass_setParamf(ALfilter
*filter
, ALenum param
, float val
)
141 case AL_HIGHPASS_GAIN
:
142 if(!(val
>= FILTER_MIN_GAIN
&& val
<= FILTER_MAX_GAIN
))
143 throw filter_exception
{AL_INVALID_VALUE
, "High-pass gain %f out of range", val
};
147 case AL_HIGHPASS_GAINLF
:
148 if(!(val
>= AL_HIGHPASS_MIN_GAINLF
&& val
<= AL_HIGHPASS_MAX_GAINLF
))
149 throw filter_exception
{AL_INVALID_VALUE
, "High-pass gainlf %f out of range", val
};
150 filter
->GainLF
= val
;
154 throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass float property 0x%04x", param
};
157 void ALhighpass_setParamfv(ALfilter
*filter
, ALenum param
, const float *vals
)
158 { ALhighpass_setParamf(filter
, param
, vals
[0]); }
160 void ALhighpass_getParami(const ALfilter
*, ALenum param
, int*)
161 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass integer property 0x%04x", param
}; }
162 void ALhighpass_getParamiv(const ALfilter
*, ALenum param
, int*)
164 throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass integer-vector property 0x%04x",
167 void ALhighpass_getParamf(const ALfilter
*filter
, ALenum param
, float *val
)
171 case AL_HIGHPASS_GAIN
:
175 case AL_HIGHPASS_GAINLF
:
176 *val
= filter
->GainLF
;
180 throw filter_exception
{AL_INVALID_ENUM
, "Invalid high-pass float property 0x%04x", param
};
183 void ALhighpass_getParamfv(const ALfilter
*filter
, ALenum param
, float *vals
)
184 { ALhighpass_getParamf(filter
, param
, vals
); }
186 DEFINE_ALFILTER_VTABLE(ALhighpass
);
189 void ALbandpass_setParami(ALfilter
*, ALenum param
, int)
190 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass integer property 0x%04x", param
}; }
191 void ALbandpass_setParamiv(ALfilter
*, ALenum param
, const int*)
193 throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass integer-vector property 0x%04x",
196 void ALbandpass_setParamf(ALfilter
*filter
, ALenum param
, float val
)
200 case AL_BANDPASS_GAIN
:
201 if(!(val
>= FILTER_MIN_GAIN
&& val
<= FILTER_MAX_GAIN
))
202 throw filter_exception
{AL_INVALID_VALUE
, "Band-pass gain %f out of range", val
};
206 case AL_BANDPASS_GAINHF
:
207 if(!(val
>= AL_BANDPASS_MIN_GAINHF
&& val
<= AL_BANDPASS_MAX_GAINHF
))
208 throw filter_exception
{AL_INVALID_VALUE
, "Band-pass gainhf %f out of range", val
};
209 filter
->GainHF
= val
;
212 case AL_BANDPASS_GAINLF
:
213 if(!(val
>= AL_BANDPASS_MIN_GAINLF
&& val
<= AL_BANDPASS_MAX_GAINLF
))
214 throw filter_exception
{AL_INVALID_VALUE
, "Band-pass gainlf %f out of range", val
};
215 filter
->GainLF
= val
;
219 throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass float property 0x%04x", param
};
222 void ALbandpass_setParamfv(ALfilter
*filter
, ALenum param
, const float *vals
)
223 { ALbandpass_setParamf(filter
, param
, vals
[0]); }
225 void ALbandpass_getParami(const ALfilter
*, ALenum param
, int*)
226 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass integer property 0x%04x", param
}; }
227 void ALbandpass_getParamiv(const ALfilter
*, ALenum param
, int*)
229 throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass integer-vector property 0x%04x",
232 void ALbandpass_getParamf(const ALfilter
*filter
, ALenum param
, float *val
)
236 case AL_BANDPASS_GAIN
:
240 case AL_BANDPASS_GAINHF
:
241 *val
= filter
->GainHF
;
244 case AL_BANDPASS_GAINLF
:
245 *val
= filter
->GainLF
;
249 throw filter_exception
{AL_INVALID_ENUM
, "Invalid band-pass float property 0x%04x", param
};
252 void ALbandpass_getParamfv(const ALfilter
*filter
, ALenum param
, float *vals
)
253 { ALbandpass_getParamf(filter
, param
, vals
); }
255 DEFINE_ALFILTER_VTABLE(ALbandpass
);
258 void ALnullfilter_setParami(ALfilter
*, ALenum param
, int)
259 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid null filter property 0x%04x", param
}; }
260 void ALnullfilter_setParamiv(ALfilter
*, ALenum param
, const int*)
261 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid null filter property 0x%04x", param
}; }
262 void ALnullfilter_setParamf(ALfilter
*, ALenum param
, float)
263 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid null filter property 0x%04x", param
}; }
264 void ALnullfilter_setParamfv(ALfilter
*, ALenum param
, const float*)
265 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid null filter property 0x%04x", param
}; }
267 void ALnullfilter_getParami(const ALfilter
*, ALenum param
, int*)
268 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid null filter property 0x%04x", param
}; }
269 void ALnullfilter_getParamiv(const ALfilter
*, ALenum param
, int*)
270 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid null filter property 0x%04x", param
}; }
271 void ALnullfilter_getParamf(const ALfilter
*, ALenum param
, float*)
272 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid null filter property 0x%04x", param
}; }
273 void ALnullfilter_getParamfv(const ALfilter
*, ALenum param
, float*)
274 { throw filter_exception
{AL_INVALID_ENUM
, "Invalid null filter property 0x%04x", param
}; }
276 DEFINE_ALFILTER_VTABLE(ALnullfilter
);
279 void InitFilterParams(ALfilter
*filter
, ALenum type
)
281 if(type
== AL_FILTER_LOWPASS
)
283 filter
->Gain
= AL_LOWPASS_DEFAULT_GAIN
;
284 filter
->GainHF
= AL_LOWPASS_DEFAULT_GAINHF
;
285 filter
->HFReference
= LOWPASSFREQREF
;
286 filter
->GainLF
= 1.0f
;
287 filter
->LFReference
= HIGHPASSFREQREF
;
288 filter
->vtab
= &ALlowpass_vtable
;
290 else if(type
== AL_FILTER_HIGHPASS
)
292 filter
->Gain
= AL_HIGHPASS_DEFAULT_GAIN
;
293 filter
->GainHF
= 1.0f
;
294 filter
->HFReference
= LOWPASSFREQREF
;
295 filter
->GainLF
= AL_HIGHPASS_DEFAULT_GAINLF
;
296 filter
->LFReference
= HIGHPASSFREQREF
;
297 filter
->vtab
= &ALhighpass_vtable
;
299 else if(type
== AL_FILTER_BANDPASS
)
301 filter
->Gain
= AL_BANDPASS_DEFAULT_GAIN
;
302 filter
->GainHF
= AL_BANDPASS_DEFAULT_GAINHF
;
303 filter
->HFReference
= LOWPASSFREQREF
;
304 filter
->GainLF
= AL_BANDPASS_DEFAULT_GAINLF
;
305 filter
->LFReference
= HIGHPASSFREQREF
;
306 filter
->vtab
= &ALbandpass_vtable
;
311 filter
->GainHF
= 1.0f
;
312 filter
->HFReference
= LOWPASSFREQREF
;
313 filter
->GainLF
= 1.0f
;
314 filter
->LFReference
= HIGHPASSFREQREF
;
315 filter
->vtab
= &ALnullfilter_vtable
;
320 bool EnsureFilters(ALCdevice
*device
, size_t needed
)
322 size_t count
{std::accumulate(device
->FilterList
.cbegin(), device
->FilterList
.cend(), size_t{0},
323 [](size_t cur
, const FilterSubList
&sublist
) noexcept
-> size_t
324 { return cur
+ static_cast<ALuint
>(POPCNT64(sublist
.FreeMask
)); }
327 while(needed
> count
)
329 if UNLIKELY(device
->FilterList
.size() >= 1<<25)
332 device
->FilterList
.emplace_back();
333 auto sublist
= device
->FilterList
.end() - 1;
334 sublist
->FreeMask
= ~0_u64
;
335 sublist
->Filters
= static_cast<ALfilter
*>(al_calloc(alignof(ALfilter
), sizeof(ALfilter
)*64));
336 if UNLIKELY(!sublist
->Filters
)
338 device
->FilterList
.pop_back();
347 ALfilter
*AllocFilter(ALCdevice
*device
)
349 auto sublist
= std::find_if(device
->FilterList
.begin(), device
->FilterList
.end(),
350 [](const FilterSubList
&entry
) noexcept
-> bool
351 { return entry
.FreeMask
!= 0; }
353 auto lidx
= static_cast<ALuint
>(std::distance(device
->FilterList
.begin(), sublist
));
354 auto slidx
= static_cast<ALuint
>(CTZ64(sublist
->FreeMask
));
356 ALfilter
*filter
{::new(sublist
->Filters
+ slidx
) ALfilter
{}};
357 InitFilterParams(filter
, AL_FILTER_NULL
);
359 /* Add 1 to avoid filter ID 0. */
360 filter
->id
= ((lidx
<<6) | slidx
) + 1;
362 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
367 void FreeFilter(ALCdevice
*device
, ALfilter
*filter
)
369 const ALuint id
{filter
->id
- 1};
370 const size_t lidx
{id
>> 6};
371 const ALuint slidx
{id
& 0x3f};
373 al::destroy_at(filter
);
375 device
->FilterList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
379 inline ALfilter
*LookupFilter(ALCdevice
*device
, ALuint id
)
381 const size_t lidx
{(id
-1) >> 6};
382 const ALuint slidx
{(id
-1) & 0x3f};
384 if UNLIKELY(lidx
>= device
->FilterList
.size())
386 FilterSubList
&sublist
= device
->FilterList
[lidx
];
387 if UNLIKELY(sublist
.FreeMask
& (1_u64
<< slidx
))
389 return sublist
.Filters
+ slidx
;
394 AL_API
void AL_APIENTRY
alGenFilters(ALsizei n
, ALuint
*filters
)
397 ContextRef context
{GetContextRef()};
398 if UNLIKELY(!context
) return;
401 context
->setError(AL_INVALID_VALUE
, "Generating %d filters", n
);
402 if UNLIKELY(n
<= 0) return;
404 ALCdevice
*device
{context
->mDevice
.get()};
405 std::lock_guard
<std::mutex
> _
{device
->EffectLock
};
406 if(!EnsureFilters(device
, static_cast<ALuint
>(n
)))
408 context
->setError(AL_OUT_OF_MEMORY
, "Failed to allocate %d filter%s", n
, (n
==1)?"":"s");
414 /* Special handling for the easy and normal case. */
415 ALfilter
*filter
{AllocFilter(device
)};
416 if(filter
) filters
[0] = filter
->id
;
420 /* Store the allocated buffer IDs in a separate local list, to avoid
421 * modifying the user storage in case of failure.
423 al::vector
<ALuint
> ids
;
424 ids
.reserve(static_cast<ALuint
>(n
));
426 ALfilter
*filter
{AllocFilter(device
)};
427 ids
.emplace_back(filter
->id
);
429 std::copy(ids
.begin(), ids
.end(), filters
);
434 AL_API
void AL_APIENTRY
alDeleteFilters(ALsizei n
, const ALuint
*filters
)
437 ContextRef context
{GetContextRef()};
438 if UNLIKELY(!context
) return;
441 context
->setError(AL_INVALID_VALUE
, "Deleting %d filters", n
);
442 if UNLIKELY(n
<= 0) return;
444 ALCdevice
*device
{context
->mDevice
.get()};
445 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
447 /* First try to find any filters that are invalid. */
448 auto validate_filter
= [device
](const ALuint fid
) -> bool
449 { return !fid
|| LookupFilter(device
, fid
) != nullptr; };
451 const ALuint
*filters_end
= filters
+ n
;
452 auto invflt
= std::find_if_not(filters
, filters_end
, validate_filter
);
453 if UNLIKELY(invflt
!= filters_end
)
455 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", *invflt
);
459 /* All good. Delete non-0 filter IDs. */
460 auto delete_filter
= [device
](const ALuint fid
) -> void
462 ALfilter
*filter
{fid
? LookupFilter(device
, fid
) : nullptr};
463 if(filter
) FreeFilter(device
, filter
);
465 std::for_each(filters
, filters_end
, delete_filter
);
469 AL_API ALboolean AL_APIENTRY
alIsFilter(ALuint filter
)
472 ContextRef context
{GetContextRef()};
475 ALCdevice
*device
{context
->mDevice
.get()};
476 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
477 if(!filter
|| LookupFilter(device
, filter
))
485 AL_API
void AL_APIENTRY
alFilteri(ALuint filter
, ALenum param
, ALint value
)
488 ContextRef context
{GetContextRef()};
489 if UNLIKELY(!context
) return;
491 ALCdevice
*device
{context
->mDevice
.get()};
492 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
494 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
496 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
499 if(param
== AL_FILTER_TYPE
)
501 if(value
== AL_FILTER_NULL
|| value
== AL_FILTER_LOWPASS
502 || value
== AL_FILTER_HIGHPASS
|| value
== AL_FILTER_BANDPASS
)
503 InitFilterParams(alfilt
, value
);
505 context
->setError(AL_INVALID_VALUE
, "Invalid filter type 0x%04x", value
);
509 /* Call the appropriate handler */
510 alfilt
->setParami(param
, value
);
512 catch(filter_exception
&e
) {
513 context
->setError(e
.errorCode(), "%s", e
.what());
519 AL_API
void AL_APIENTRY
alFilteriv(ALuint filter
, ALenum param
, const ALint
*values
)
525 alFilteri(filter
, param
, values
[0]);
529 ContextRef context
{GetContextRef()};
530 if UNLIKELY(!context
) return;
532 ALCdevice
*device
{context
->mDevice
.get()};
533 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
535 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
537 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
540 /* Call the appropriate handler */
541 alfilt
->setParamiv(param
, values
);
543 catch(filter_exception
&e
) {
544 context
->setError(e
.errorCode(), "%s", e
.what());
549 AL_API
void AL_APIENTRY
alFilterf(ALuint filter
, ALenum param
, ALfloat value
)
552 ContextRef context
{GetContextRef()};
553 if UNLIKELY(!context
) return;
555 ALCdevice
*device
{context
->mDevice
.get()};
556 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
558 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
560 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
563 /* Call the appropriate handler */
564 alfilt
->setParamf(param
, value
);
566 catch(filter_exception
&e
) {
567 context
->setError(e
.errorCode(), "%s", e
.what());
572 AL_API
void AL_APIENTRY
alFilterfv(ALuint filter
, ALenum param
, const ALfloat
*values
)
575 ContextRef context
{GetContextRef()};
576 if UNLIKELY(!context
) return;
578 ALCdevice
*device
{context
->mDevice
.get()};
579 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
581 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
583 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
586 /* Call the appropriate handler */
587 alfilt
->setParamfv(param
, values
);
589 catch(filter_exception
&e
) {
590 context
->setError(e
.errorCode(), "%s", e
.what());
595 AL_API
void AL_APIENTRY
alGetFilteri(ALuint filter
, ALenum param
, ALint
*value
)
598 ContextRef context
{GetContextRef()};
599 if UNLIKELY(!context
) return;
601 ALCdevice
*device
{context
->mDevice
.get()};
602 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
604 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
606 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
609 if(param
== AL_FILTER_TYPE
)
610 *value
= alfilt
->type
;
613 /* Call the appropriate handler */
614 alfilt
->getParami(param
, value
);
616 catch(filter_exception
&e
) {
617 context
->setError(e
.errorCode(), "%s", e
.what());
623 AL_API
void AL_APIENTRY
alGetFilteriv(ALuint filter
, ALenum param
, ALint
*values
)
629 alGetFilteri(filter
, param
, values
);
633 ContextRef context
{GetContextRef()};
634 if UNLIKELY(!context
) return;
636 ALCdevice
*device
{context
->mDevice
.get()};
637 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
639 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
641 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
644 /* Call the appropriate handler */
645 alfilt
->getParamiv(param
, values
);
647 catch(filter_exception
&e
) {
648 context
->setError(e
.errorCode(), "%s", e
.what());
653 AL_API
void AL_APIENTRY
alGetFilterf(ALuint filter
, ALenum param
, ALfloat
*value
)
656 ContextRef context
{GetContextRef()};
657 if UNLIKELY(!context
) return;
659 ALCdevice
*device
{context
->mDevice
.get()};
660 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
662 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
664 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
667 /* Call the appropriate handler */
668 alfilt
->getParamf(param
, value
);
670 catch(filter_exception
&e
) {
671 context
->setError(e
.errorCode(), "%s", e
.what());
676 AL_API
void AL_APIENTRY
alGetFilterfv(ALuint filter
, ALenum param
, ALfloat
*values
)
679 ContextRef context
{GetContextRef()};
680 if UNLIKELY(!context
) return;
682 ALCdevice
*device
{context
->mDevice
.get()};
683 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
685 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
687 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
690 /* Call the appropriate handler */
691 alfilt
->getParamfv(param
, values
);
693 catch(filter_exception
&e
) {
694 context
->setError(e
.errorCode(), "%s", e
.what());
700 FilterSubList::~FilterSubList()
702 uint64_t usemask
{~FreeMask
};
705 ALsizei idx
= CTZ64(usemask
);
706 al::destroy_at(Filters
+idx
);
707 usemask
&= ~(1_u64
<< idx
);