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
>(PopCount(sublist
.FreeMask
)); })};
326 while(needed
> count
)
328 if UNLIKELY(device
->FilterList
.size() >= 1<<25)
331 device
->FilterList
.emplace_back();
332 auto sublist
= device
->FilterList
.end() - 1;
333 sublist
->FreeMask
= ~0_u64
;
334 sublist
->Filters
= static_cast<ALfilter
*>(al_calloc(alignof(ALfilter
), sizeof(ALfilter
)*64));
335 if UNLIKELY(!sublist
->Filters
)
337 device
->FilterList
.pop_back();
346 ALfilter
*AllocFilter(ALCdevice
*device
)
348 auto sublist
= std::find_if(device
->FilterList
.begin(), device
->FilterList
.end(),
349 [](const FilterSubList
&entry
) noexcept
-> bool
350 { return entry
.FreeMask
!= 0; }
352 auto lidx
= static_cast<ALuint
>(std::distance(device
->FilterList
.begin(), sublist
));
353 auto slidx
= static_cast<ALuint
>(CountTrailingZeros(sublist
->FreeMask
));
355 ALfilter
*filter
{::new(sublist
->Filters
+ slidx
) ALfilter
{}};
356 InitFilterParams(filter
, AL_FILTER_NULL
);
358 /* Add 1 to avoid filter ID 0. */
359 filter
->id
= ((lidx
<<6) | slidx
) + 1;
361 sublist
->FreeMask
&= ~(1_u64
<< slidx
);
366 void FreeFilter(ALCdevice
*device
, ALfilter
*filter
)
368 const ALuint id
{filter
->id
- 1};
369 const size_t lidx
{id
>> 6};
370 const ALuint slidx
{id
& 0x3f};
372 al::destroy_at(filter
);
374 device
->FilterList
[lidx
].FreeMask
|= 1_u64
<< slidx
;
378 inline ALfilter
*LookupFilter(ALCdevice
*device
, ALuint id
)
380 const size_t lidx
{(id
-1) >> 6};
381 const ALuint slidx
{(id
-1) & 0x3f};
383 if UNLIKELY(lidx
>= device
->FilterList
.size())
385 FilterSubList
&sublist
= device
->FilterList
[lidx
];
386 if UNLIKELY(sublist
.FreeMask
& (1_u64
<< slidx
))
388 return sublist
.Filters
+ slidx
;
393 AL_API
void AL_APIENTRY
alGenFilters(ALsizei n
, ALuint
*filters
)
396 ContextRef context
{GetContextRef()};
397 if UNLIKELY(!context
) return;
400 context
->setError(AL_INVALID_VALUE
, "Generating %d filters", n
);
401 if UNLIKELY(n
<= 0) return;
403 ALCdevice
*device
{context
->mDevice
.get()};
404 std::lock_guard
<std::mutex
> _
{device
->EffectLock
};
405 if(!EnsureFilters(device
, static_cast<ALuint
>(n
)))
407 context
->setError(AL_OUT_OF_MEMORY
, "Failed to allocate %d filter%s", n
, (n
==1)?"":"s");
413 /* Special handling for the easy and normal case. */
414 ALfilter
*filter
{AllocFilter(device
)};
415 if(filter
) filters
[0] = filter
->id
;
419 /* Store the allocated buffer IDs in a separate local list, to avoid
420 * modifying the user storage in case of failure.
422 al::vector
<ALuint
> ids
;
423 ids
.reserve(static_cast<ALuint
>(n
));
425 ALfilter
*filter
{AllocFilter(device
)};
426 ids
.emplace_back(filter
->id
);
428 std::copy(ids
.begin(), ids
.end(), filters
);
433 AL_API
void AL_APIENTRY
alDeleteFilters(ALsizei n
, const ALuint
*filters
)
436 ContextRef context
{GetContextRef()};
437 if UNLIKELY(!context
) return;
440 context
->setError(AL_INVALID_VALUE
, "Deleting %d filters", n
);
441 if UNLIKELY(n
<= 0) return;
443 ALCdevice
*device
{context
->mDevice
.get()};
444 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
446 /* First try to find any filters that are invalid. */
447 auto validate_filter
= [device
](const ALuint fid
) -> bool
448 { return !fid
|| LookupFilter(device
, fid
) != nullptr; };
450 const ALuint
*filters_end
= filters
+ n
;
451 auto invflt
= std::find_if_not(filters
, filters_end
, validate_filter
);
452 if UNLIKELY(invflt
!= filters_end
)
454 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", *invflt
);
458 /* All good. Delete non-0 filter IDs. */
459 auto delete_filter
= [device
](const ALuint fid
) -> void
461 ALfilter
*filter
{fid
? LookupFilter(device
, fid
) : nullptr};
462 if(filter
) FreeFilter(device
, filter
);
464 std::for_each(filters
, filters_end
, delete_filter
);
468 AL_API ALboolean AL_APIENTRY
alIsFilter(ALuint filter
)
471 ContextRef context
{GetContextRef()};
474 ALCdevice
*device
{context
->mDevice
.get()};
475 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
476 if(!filter
|| LookupFilter(device
, filter
))
484 AL_API
void AL_APIENTRY
alFilteri(ALuint filter
, ALenum param
, ALint value
)
487 ContextRef context
{GetContextRef()};
488 if UNLIKELY(!context
) return;
490 ALCdevice
*device
{context
->mDevice
.get()};
491 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
493 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
495 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
498 if(param
== AL_FILTER_TYPE
)
500 if(value
== AL_FILTER_NULL
|| value
== AL_FILTER_LOWPASS
501 || value
== AL_FILTER_HIGHPASS
|| value
== AL_FILTER_BANDPASS
)
502 InitFilterParams(alfilt
, value
);
504 context
->setError(AL_INVALID_VALUE
, "Invalid filter type 0x%04x", value
);
508 /* Call the appropriate handler */
509 alfilt
->setParami(param
, value
);
511 catch(filter_exception
&e
) {
512 context
->setError(e
.errorCode(), "%s", e
.what());
518 AL_API
void AL_APIENTRY
alFilteriv(ALuint filter
, ALenum param
, const ALint
*values
)
524 alFilteri(filter
, param
, values
[0]);
528 ContextRef context
{GetContextRef()};
529 if UNLIKELY(!context
) return;
531 ALCdevice
*device
{context
->mDevice
.get()};
532 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
534 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
536 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
539 /* Call the appropriate handler */
540 alfilt
->setParamiv(param
, values
);
542 catch(filter_exception
&e
) {
543 context
->setError(e
.errorCode(), "%s", e
.what());
548 AL_API
void AL_APIENTRY
alFilterf(ALuint filter
, ALenum param
, ALfloat value
)
551 ContextRef context
{GetContextRef()};
552 if UNLIKELY(!context
) return;
554 ALCdevice
*device
{context
->mDevice
.get()};
555 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
557 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
559 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
562 /* Call the appropriate handler */
563 alfilt
->setParamf(param
, value
);
565 catch(filter_exception
&e
) {
566 context
->setError(e
.errorCode(), "%s", e
.what());
571 AL_API
void AL_APIENTRY
alFilterfv(ALuint filter
, ALenum param
, const ALfloat
*values
)
574 ContextRef context
{GetContextRef()};
575 if UNLIKELY(!context
) return;
577 ALCdevice
*device
{context
->mDevice
.get()};
578 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
580 ALfilter
*alfilt
{LookupFilter(device
, filter
)};
582 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
585 /* Call the appropriate handler */
586 alfilt
->setParamfv(param
, values
);
588 catch(filter_exception
&e
) {
589 context
->setError(e
.errorCode(), "%s", e
.what());
594 AL_API
void AL_APIENTRY
alGetFilteri(ALuint filter
, ALenum param
, ALint
*value
)
597 ContextRef context
{GetContextRef()};
598 if UNLIKELY(!context
) return;
600 ALCdevice
*device
{context
->mDevice
.get()};
601 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
603 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
605 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
608 if(param
== AL_FILTER_TYPE
)
609 *value
= alfilt
->type
;
612 /* Call the appropriate handler */
613 alfilt
->getParami(param
, value
);
615 catch(filter_exception
&e
) {
616 context
->setError(e
.errorCode(), "%s", e
.what());
622 AL_API
void AL_APIENTRY
alGetFilteriv(ALuint filter
, ALenum param
, ALint
*values
)
628 alGetFilteri(filter
, param
, values
);
632 ContextRef context
{GetContextRef()};
633 if UNLIKELY(!context
) return;
635 ALCdevice
*device
{context
->mDevice
.get()};
636 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
638 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
640 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
643 /* Call the appropriate handler */
644 alfilt
->getParamiv(param
, values
);
646 catch(filter_exception
&e
) {
647 context
->setError(e
.errorCode(), "%s", e
.what());
652 AL_API
void AL_APIENTRY
alGetFilterf(ALuint filter
, ALenum param
, ALfloat
*value
)
655 ContextRef context
{GetContextRef()};
656 if UNLIKELY(!context
) return;
658 ALCdevice
*device
{context
->mDevice
.get()};
659 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
661 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
663 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
666 /* Call the appropriate handler */
667 alfilt
->getParamf(param
, value
);
669 catch(filter_exception
&e
) {
670 context
->setError(e
.errorCode(), "%s", e
.what());
675 AL_API
void AL_APIENTRY
alGetFilterfv(ALuint filter
, ALenum param
, ALfloat
*values
)
678 ContextRef context
{GetContextRef()};
679 if UNLIKELY(!context
) return;
681 ALCdevice
*device
{context
->mDevice
.get()};
682 std::lock_guard
<std::mutex
> _
{device
->FilterLock
};
684 const ALfilter
*alfilt
{LookupFilter(device
, filter
)};
686 context
->setError(AL_INVALID_NAME
, "Invalid filter ID %u", filter
);
689 /* Call the appropriate handler */
690 alfilt
->getParamfv(param
, values
);
692 catch(filter_exception
&e
) {
693 context
->setError(e
.errorCode(), "%s", e
.what());
699 FilterSubList::~FilterSubList()
701 uint64_t usemask
{~FreeMask
};
704 const ALsizei idx
{CountTrailingZeros(usemask
)};
705 al::destroy_at(Filters
+idx
);
706 usemask
&= ~(1_u64
<< idx
);