1 /**
2  * OpenAL cross platform audio library
3  * Copyright (C) 1999-2000 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.
8  *
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.
13  *
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
19  */
20 
21 #include "config.h"
22 
23 #include "version.h"
24 
25 #include <atomic>
26 #include <cmath>
27 #include <cstdlib>
28 #include <cstring>
29 #include <mutex>
30 
31 #include "AL/al.h"
32 #include "AL/alc.h"
33 #include "AL/alext.h"
34 
35 #include "alcontext.h"
36 #include "almalloc.h"
37 #include "alnumeric.h"
38 #include "alspan.h"
39 #include "alu.h"
40 #include "atomic.h"
41 #include "core/except.h"
42 #include "event.h"
43 #include "inprogext.h"
44 #include "opthelpers.h"
45 #include "strutils.h"
46 #include "voice.h"
47 
48 
49 namespace {
50 
51 constexpr ALchar alVendor[] = "OpenAL Community";
52 constexpr ALchar alVersion[] = "1.1 ALSOFT " ALSOFT_VERSION;
53 constexpr ALchar alRenderer[] = "OpenAL Soft";
54 
55 // Error Messages
56 constexpr ALchar alNoError[] = "No Error";
57 constexpr ALchar alErrInvalidName[] = "Invalid Name";
58 constexpr ALchar alErrInvalidEnum[] = "Invalid Enum";
59 constexpr ALchar alErrInvalidValue[] = "Invalid Value";
60 constexpr ALchar alErrInvalidOp[] = "Invalid Operation";
61 constexpr ALchar alErrOutOfMemory[] = "Out of Memory";
62 
63 /* Resampler strings */
64 template<Resampler rtype> struct ResamplerName { };
65 template<> struct ResamplerName<Resampler::Point>
Get__anon5effb9350111::ResamplerName66 { static constexpr const ALchar *Get() noexcept { return "Nearest"; } };
67 template<> struct ResamplerName<Resampler::Linear>
Get__anon5effb9350111::ResamplerName68 { static constexpr const ALchar *Get() noexcept { return "Linear"; } };
69 template<> struct ResamplerName<Resampler::Cubic>
Get__anon5effb9350111::ResamplerName70 { static constexpr const ALchar *Get() noexcept { return "Cubic"; } };
71 template<> struct ResamplerName<Resampler::FastBSinc12>
Get__anon5effb9350111::ResamplerName72 { static constexpr const ALchar *Get() noexcept { return "11th order Sinc (fast)"; } };
73 template<> struct ResamplerName<Resampler::BSinc12>
Get__anon5effb9350111::ResamplerName74 { static constexpr const ALchar *Get() noexcept { return "11th order Sinc"; } };
75 template<> struct ResamplerName<Resampler::FastBSinc24>
Get__anon5effb9350111::ResamplerName76 { static constexpr const ALchar *Get() noexcept { return "23rd order Sinc (fast)"; } };
77 template<> struct ResamplerName<Resampler::BSinc24>
Get__anon5effb9350111::ResamplerName78 { static constexpr const ALchar *Get() noexcept { return "23rd order Sinc"; } };
79 
GetResamplerName(const Resampler rtype)80 const ALchar *GetResamplerName(const Resampler rtype)
81 {
82 #define HANDLE_RESAMPLER(r) case r: return ResamplerName<r>::Get()
83     switch(rtype)
84     {
85     HANDLE_RESAMPLER(Resampler::Point);
86     HANDLE_RESAMPLER(Resampler::Linear);
87     HANDLE_RESAMPLER(Resampler::Cubic);
88     HANDLE_RESAMPLER(Resampler::FastBSinc12);
89     HANDLE_RESAMPLER(Resampler::BSinc12);
90     HANDLE_RESAMPLER(Resampler::FastBSinc24);
91     HANDLE_RESAMPLER(Resampler::BSinc24);
92     }
93 #undef HANDLE_RESAMPLER
94     /* Should never get here. */
95     throw std::runtime_error{"Unexpected resampler index"};
96 }
97 
DistanceModelFromALenum(ALenum model)98 al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
99 {
100     switch(model)
101     {
102     case AL_NONE: return al::make_optional(DistanceModel::Disable);
103     case AL_INVERSE_DISTANCE: return al::make_optional(DistanceModel::Inverse);
104     case AL_INVERSE_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::InverseClamped);
105     case AL_LINEAR_DISTANCE: return al::make_optional(DistanceModel::Linear);
106     case AL_LINEAR_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::LinearClamped);
107     case AL_EXPONENT_DISTANCE: return al::make_optional(DistanceModel::Exponent);
108     case AL_EXPONENT_DISTANCE_CLAMPED: return al::make_optional(DistanceModel::ExponentClamped);
109     }
110     return al::nullopt;
111 }
ALenumFromDistanceModel(DistanceModel model)112 ALenum ALenumFromDistanceModel(DistanceModel model)
113 {
114     switch(model)
115     {
116     case DistanceModel::Disable: return AL_NONE;
117     case DistanceModel::Inverse: return AL_INVERSE_DISTANCE;
118     case DistanceModel::InverseClamped: return AL_INVERSE_DISTANCE_CLAMPED;
119     case DistanceModel::Linear: return AL_LINEAR_DISTANCE;
120     case DistanceModel::LinearClamped: return AL_LINEAR_DISTANCE_CLAMPED;
121     case DistanceModel::Exponent: return AL_EXPONENT_DISTANCE;
122     case DistanceModel::ExponentClamped: return AL_EXPONENT_DISTANCE_CLAMPED;
123     }
124     throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast<int>(model))};
125 }
126 
127 } // namespace
128 
129 /* WARNING: Non-standard export! Not part of any extension, or exposed in the
130  * alcFunctions list.
131  */
alsoft_get_version(void)132 extern "C" AL_API const ALchar* AL_APIENTRY alsoft_get_version(void)
133 START_API_FUNC
134 {
135     static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION");
136     if(spoof) return spoof->c_str();
137     return ALSOFT_VERSION;
138 }
139 END_API_FUNC
140 
141 #define DO_UPDATEPROPS() do {                                                 \
142     if(!context->mDeferUpdates.load(std::memory_order_acquire))               \
143         UpdateContextProps(context.get());                                    \
144     else                                                                      \
145         context->mPropsClean.clear(std::memory_order_release);                \
146 } while(0)
147 
148 
alEnable(ALenum capability)149 AL_API void AL_APIENTRY alEnable(ALenum capability)
150 START_API_FUNC
151 {
152     ContextRef context{GetContextRef()};
153     if UNLIKELY(!context) return;
154 
155     std::lock_guard<std::mutex> _{context->mPropLock};
156     switch(capability)
157     {
158     case AL_SOURCE_DISTANCE_MODEL:
159         context->mSourceDistanceModel = true;
160         DO_UPDATEPROPS();
161         break;
162 
163     default:
164         context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability);
165     }
166 }
167 END_API_FUNC
168 
alDisable(ALenum capability)169 AL_API void AL_APIENTRY alDisable(ALenum capability)
170 START_API_FUNC
171 {
172     ContextRef context{GetContextRef()};
173     if UNLIKELY(!context) return;
174 
175     std::lock_guard<std::mutex> _{context->mPropLock};
176     switch(capability)
177     {
178     case AL_SOURCE_DISTANCE_MODEL:
179         context->mSourceDistanceModel = false;
180         DO_UPDATEPROPS();
181         break;
182 
183     default:
184         context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability);
185     }
186 }
187 END_API_FUNC
188 
alIsEnabled(ALenum capability)189 AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability)
190 START_API_FUNC
191 {
192     ContextRef context{GetContextRef()};
193     if UNLIKELY(!context) return AL_FALSE;
194 
195     std::lock_guard<std::mutex> _{context->mPropLock};
196     ALboolean value{AL_FALSE};
197     switch(capability)
198     {
199     case AL_SOURCE_DISTANCE_MODEL:
200         value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
201         break;
202 
203     default:
204         context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
205     }
206 
207     return value;
208 }
209 END_API_FUNC
210 
alGetBoolean(ALenum pname)211 AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname)
212 START_API_FUNC
213 {
214     ContextRef context{GetContextRef()};
215     if UNLIKELY(!context) return AL_FALSE;
216 
217     std::lock_guard<std::mutex> _{context->mPropLock};
218     ALboolean value{AL_FALSE};
219     switch(pname)
220     {
221     case AL_DOPPLER_FACTOR:
222         if(context->mDopplerFactor != 0.0f)
223             value = AL_TRUE;
224         break;
225 
226     case AL_DOPPLER_VELOCITY:
227         if(context->mDopplerVelocity != 0.0f)
228             value = AL_TRUE;
229         break;
230 
231     case AL_DISTANCE_MODEL:
232         if(context->mDistanceModel == DistanceModel::Default)
233             value = AL_TRUE;
234         break;
235 
236     case AL_SPEED_OF_SOUND:
237         if(context->mSpeedOfSound != 0.0f)
238             value = AL_TRUE;
239         break;
240 
241     case AL_DEFERRED_UPDATES_SOFT:
242         if(context->mDeferUpdates.load(std::memory_order_acquire))
243             value = AL_TRUE;
244         break;
245 
246     case AL_GAIN_LIMIT_SOFT:
247         if(GainMixMax/context->mGainBoost != 0.0f)
248             value = AL_TRUE;
249         break;
250 
251     case AL_NUM_RESAMPLERS_SOFT:
252         /* Always non-0. */
253         value = AL_TRUE;
254         break;
255 
256     case AL_DEFAULT_RESAMPLER_SOFT:
257         value = static_cast<int>(ResamplerDefault) ? AL_TRUE : AL_FALSE;
258         break;
259 
260     default:
261         context->setError(AL_INVALID_VALUE, "Invalid boolean property 0x%04x", pname);
262     }
263 
264     return value;
265 }
266 END_API_FUNC
267 
alGetDouble(ALenum pname)268 AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
269 START_API_FUNC
270 {
271     ContextRef context{GetContextRef()};
272     if UNLIKELY(!context) return 0.0;
273 
274     std::lock_guard<std::mutex> _{context->mPropLock};
275     ALdouble value{0.0};
276     switch(pname)
277     {
278     case AL_DOPPLER_FACTOR:
279         value = context->mDopplerFactor;
280         break;
281 
282     case AL_DOPPLER_VELOCITY:
283         value = context->mDopplerVelocity;
284         break;
285 
286     case AL_DISTANCE_MODEL:
287         value = static_cast<ALdouble>(ALenumFromDistanceModel(context->mDistanceModel));
288         break;
289 
290     case AL_SPEED_OF_SOUND:
291         value = context->mSpeedOfSound;
292         break;
293 
294     case AL_DEFERRED_UPDATES_SOFT:
295         if(context->mDeferUpdates.load(std::memory_order_acquire))
296             value = static_cast<ALdouble>(AL_TRUE);
297         break;
298 
299     case AL_GAIN_LIMIT_SOFT:
300         value = ALdouble{GainMixMax}/context->mGainBoost;
301         break;
302 
303     case AL_NUM_RESAMPLERS_SOFT:
304         value = static_cast<ALdouble>(Resampler::Max) + 1.0;
305         break;
306 
307     case AL_DEFAULT_RESAMPLER_SOFT:
308         value = static_cast<ALdouble>(ResamplerDefault);
309         break;
310 
311     default:
312         context->setError(AL_INVALID_VALUE, "Invalid double property 0x%04x", pname);
313     }
314 
315     return value;
316 }
317 END_API_FUNC
318 
alGetFloat(ALenum pname)319 AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
320 START_API_FUNC
321 {
322     ContextRef context{GetContextRef()};
323     if UNLIKELY(!context) return 0.0f;
324 
325     std::lock_guard<std::mutex> _{context->mPropLock};
326     ALfloat value{0.0f};
327     switch(pname)
328     {
329     case AL_DOPPLER_FACTOR:
330         value = context->mDopplerFactor;
331         break;
332 
333     case AL_DOPPLER_VELOCITY:
334         value = context->mDopplerVelocity;
335         break;
336 
337     case AL_DISTANCE_MODEL:
338         value = static_cast<ALfloat>(ALenumFromDistanceModel(context->mDistanceModel));
339         break;
340 
341     case AL_SPEED_OF_SOUND:
342         value = context->mSpeedOfSound;
343         break;
344 
345     case AL_DEFERRED_UPDATES_SOFT:
346         if(context->mDeferUpdates.load(std::memory_order_acquire))
347             value = static_cast<ALfloat>(AL_TRUE);
348         break;
349 
350     case AL_GAIN_LIMIT_SOFT:
351         value = GainMixMax/context->mGainBoost;
352         break;
353 
354     case AL_NUM_RESAMPLERS_SOFT:
355         value = static_cast<ALfloat>(Resampler::Max) + 1.0f;
356         break;
357 
358     case AL_DEFAULT_RESAMPLER_SOFT:
359         value = static_cast<ALfloat>(ResamplerDefault);
360         break;
361 
362     default:
363         context->setError(AL_INVALID_VALUE, "Invalid float property 0x%04x", pname);
364     }
365 
366     return value;
367 }
368 END_API_FUNC
369 
alGetInteger(ALenum pname)370 AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
371 START_API_FUNC
372 {
373     ContextRef context{GetContextRef()};
374     if UNLIKELY(!context) return 0;
375 
376     std::lock_guard<std::mutex> _{context->mPropLock};
377     ALint value{0};
378     switch(pname)
379     {
380     case AL_DOPPLER_FACTOR:
381         value = static_cast<ALint>(context->mDopplerFactor);
382         break;
383 
384     case AL_DOPPLER_VELOCITY:
385         value = static_cast<ALint>(context->mDopplerVelocity);
386         break;
387 
388     case AL_DISTANCE_MODEL:
389         value = ALenumFromDistanceModel(context->mDistanceModel);
390         break;
391 
392     case AL_SPEED_OF_SOUND:
393         value = static_cast<ALint>(context->mSpeedOfSound);
394         break;
395 
396     case AL_DEFERRED_UPDATES_SOFT:
397         if(context->mDeferUpdates.load(std::memory_order_acquire))
398             value = AL_TRUE;
399         break;
400 
401     case AL_GAIN_LIMIT_SOFT:
402         value = static_cast<ALint>(GainMixMax/context->mGainBoost);
403         break;
404 
405     case AL_NUM_RESAMPLERS_SOFT:
406         value = static_cast<int>(Resampler::Max) + 1;
407         break;
408 
409     case AL_DEFAULT_RESAMPLER_SOFT:
410         value = static_cast<int>(ResamplerDefault);
411         break;
412 
413     default:
414         context->setError(AL_INVALID_VALUE, "Invalid integer property 0x%04x", pname);
415     }
416 
417     return value;
418 }
419 END_API_FUNC
420 
alGetInteger64SOFT(ALenum pname)421 extern "C" AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
422 START_API_FUNC
423 {
424     ContextRef context{GetContextRef()};
425     if UNLIKELY(!context) return 0_i64;
426 
427     std::lock_guard<std::mutex> _{context->mPropLock};
428     ALint64SOFT value{0};
429     switch(pname)
430     {
431     case AL_DOPPLER_FACTOR:
432         value = static_cast<ALint64SOFT>(context->mDopplerFactor);
433         break;
434 
435     case AL_DOPPLER_VELOCITY:
436         value = static_cast<ALint64SOFT>(context->mDopplerVelocity);
437         break;
438 
439     case AL_DISTANCE_MODEL:
440         value = ALenumFromDistanceModel(context->mDistanceModel);
441         break;
442 
443     case AL_SPEED_OF_SOUND:
444         value = static_cast<ALint64SOFT>(context->mSpeedOfSound);
445         break;
446 
447     case AL_DEFERRED_UPDATES_SOFT:
448         if(context->mDeferUpdates.load(std::memory_order_acquire))
449             value = AL_TRUE;
450         break;
451 
452     case AL_GAIN_LIMIT_SOFT:
453         value = static_cast<ALint64SOFT>(GainMixMax/context->mGainBoost);
454         break;
455 
456     case AL_NUM_RESAMPLERS_SOFT:
457         value = static_cast<ALint64SOFT>(Resampler::Max) + 1;
458         break;
459 
460     case AL_DEFAULT_RESAMPLER_SOFT:
461         value = static_cast<ALint64SOFT>(ResamplerDefault);
462         break;
463 
464     default:
465         context->setError(AL_INVALID_VALUE, "Invalid integer64 property 0x%04x", pname);
466     }
467 
468     return value;
469 }
470 END_API_FUNC
471 
alGetPointerSOFT(ALenum pname)472 AL_API ALvoid* AL_APIENTRY alGetPointerSOFT(ALenum pname)
473 START_API_FUNC
474 {
475     ContextRef context{GetContextRef()};
476     if UNLIKELY(!context) return nullptr;
477 
478     std::lock_guard<std::mutex> _{context->mPropLock};
479     void *value{nullptr};
480     switch(pname)
481     {
482     case AL_EVENT_CALLBACK_FUNCTION_SOFT:
483         value = reinterpret_cast<void*>(context->mEventCb);
484         break;
485 
486     case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
487         value = context->mEventParam;
488         break;
489 
490     default:
491         context->setError(AL_INVALID_VALUE, "Invalid pointer property 0x%04x", pname);
492     }
493 
494     return value;
495 }
496 END_API_FUNC
497 
alGetBooleanv(ALenum pname,ALboolean * values)498 AL_API void AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values)
499 START_API_FUNC
500 {
501     if(values)
502     {
503         switch(pname)
504         {
505             case AL_DOPPLER_FACTOR:
506             case AL_DOPPLER_VELOCITY:
507             case AL_DISTANCE_MODEL:
508             case AL_SPEED_OF_SOUND:
509             case AL_DEFERRED_UPDATES_SOFT:
510             case AL_GAIN_LIMIT_SOFT:
511             case AL_NUM_RESAMPLERS_SOFT:
512             case AL_DEFAULT_RESAMPLER_SOFT:
513                 values[0] = alGetBoolean(pname);
514                 return;
515         }
516     }
517 
518     ContextRef context{GetContextRef()};
519     if UNLIKELY(!context) return;
520 
521     if(!values)
522         context->setError(AL_INVALID_VALUE, "NULL pointer");
523     else switch(pname)
524     {
525     default:
526         context->setError(AL_INVALID_VALUE, "Invalid boolean-vector property 0x%04x", pname);
527     }
528 }
529 END_API_FUNC
530 
alGetDoublev(ALenum pname,ALdouble * values)531 AL_API void AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values)
532 START_API_FUNC
533 {
534     if(values)
535     {
536         switch(pname)
537         {
538             case AL_DOPPLER_FACTOR:
539             case AL_DOPPLER_VELOCITY:
540             case AL_DISTANCE_MODEL:
541             case AL_SPEED_OF_SOUND:
542             case AL_DEFERRED_UPDATES_SOFT:
543             case AL_GAIN_LIMIT_SOFT:
544             case AL_NUM_RESAMPLERS_SOFT:
545             case AL_DEFAULT_RESAMPLER_SOFT:
546                 values[0] = alGetDouble(pname);
547                 return;
548         }
549     }
550 
551     ContextRef context{GetContextRef()};
552     if UNLIKELY(!context) return;
553 
554     if(!values)
555         context->setError(AL_INVALID_VALUE, "NULL pointer");
556     else switch(pname)
557     {
558     default:
559         context->setError(AL_INVALID_VALUE, "Invalid double-vector property 0x%04x", pname);
560     }
561 }
562 END_API_FUNC
563 
alGetFloatv(ALenum pname,ALfloat * values)564 AL_API void AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values)
565 START_API_FUNC
566 {
567     if(values)
568     {
569         switch(pname)
570         {
571             case AL_DOPPLER_FACTOR:
572             case AL_DOPPLER_VELOCITY:
573             case AL_DISTANCE_MODEL:
574             case AL_SPEED_OF_SOUND:
575             case AL_DEFERRED_UPDATES_SOFT:
576             case AL_GAIN_LIMIT_SOFT:
577             case AL_NUM_RESAMPLERS_SOFT:
578             case AL_DEFAULT_RESAMPLER_SOFT:
579                 values[0] = alGetFloat(pname);
580                 return;
581         }
582     }
583 
584     ContextRef context{GetContextRef()};
585     if UNLIKELY(!context) return;
586 
587     if(!values)
588         context->setError(AL_INVALID_VALUE, "NULL pointer");
589     else switch(pname)
590     {
591     default:
592         context->setError(AL_INVALID_VALUE, "Invalid float-vector property 0x%04x", pname);
593     }
594 }
595 END_API_FUNC
596 
alGetIntegerv(ALenum pname,ALint * values)597 AL_API void AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
598 START_API_FUNC
599 {
600     if(values)
601     {
602         switch(pname)
603         {
604             case AL_DOPPLER_FACTOR:
605             case AL_DOPPLER_VELOCITY:
606             case AL_DISTANCE_MODEL:
607             case AL_SPEED_OF_SOUND:
608             case AL_DEFERRED_UPDATES_SOFT:
609             case AL_GAIN_LIMIT_SOFT:
610             case AL_NUM_RESAMPLERS_SOFT:
611             case AL_DEFAULT_RESAMPLER_SOFT:
612                 values[0] = alGetInteger(pname);
613                 return;
614         }
615     }
616 
617     ContextRef context{GetContextRef()};
618     if UNLIKELY(!context) return;
619 
620     if(!values)
621         context->setError(AL_INVALID_VALUE, "NULL pointer");
622     else switch(pname)
623     {
624     default:
625         context->setError(AL_INVALID_VALUE, "Invalid integer-vector property 0x%04x", pname);
626     }
627 }
628 END_API_FUNC
629 
alGetInteger64vSOFT(ALenum pname,ALint64SOFT * values)630 extern "C" AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
631 START_API_FUNC
632 {
633     if(values)
634     {
635         switch(pname)
636         {
637             case AL_DOPPLER_FACTOR:
638             case AL_DOPPLER_VELOCITY:
639             case AL_DISTANCE_MODEL:
640             case AL_SPEED_OF_SOUND:
641             case AL_DEFERRED_UPDATES_SOFT:
642             case AL_GAIN_LIMIT_SOFT:
643             case AL_NUM_RESAMPLERS_SOFT:
644             case AL_DEFAULT_RESAMPLER_SOFT:
645                 values[0] = alGetInteger64SOFT(pname);
646                 return;
647         }
648     }
649 
650     ContextRef context{GetContextRef()};
651     if UNLIKELY(!context) return;
652 
653     if(!values)
654         context->setError(AL_INVALID_VALUE, "NULL pointer");
655     else switch(pname)
656     {
657     default:
658         context->setError(AL_INVALID_VALUE, "Invalid integer64-vector property 0x%04x", pname);
659     }
660 }
661 END_API_FUNC
662 
alGetPointervSOFT(ALenum pname,ALvoid ** values)663 AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, ALvoid **values)
664 START_API_FUNC
665 {
666     if(values)
667     {
668         switch(pname)
669         {
670             case AL_EVENT_CALLBACK_FUNCTION_SOFT:
671             case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
672                 values[0] = alGetPointerSOFT(pname);
673                 return;
674         }
675     }
676 
677     ContextRef context{GetContextRef()};
678     if UNLIKELY(!context) return;
679 
680     if(!values)
681         context->setError(AL_INVALID_VALUE, "NULL pointer");
682     else switch(pname)
683     {
684     default:
685         context->setError(AL_INVALID_VALUE, "Invalid pointer-vector property 0x%04x", pname);
686     }
687 }
688 END_API_FUNC
689 
alGetString(ALenum pname)690 AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname)
691 START_API_FUNC
692 {
693     ContextRef context{GetContextRef()};
694     if UNLIKELY(!context) return nullptr;
695 
696     const ALchar *value{nullptr};
697     switch(pname)
698     {
699     case AL_VENDOR:
700         value = alVendor;
701         break;
702 
703     case AL_VERSION:
704         value = alVersion;
705         break;
706 
707     case AL_RENDERER:
708         value = alRenderer;
709         break;
710 
711     case AL_EXTENSIONS:
712         value = context->mExtensionList;
713         break;
714 
715     case AL_NO_ERROR:
716         value = alNoError;
717         break;
718 
719     case AL_INVALID_NAME:
720         value = alErrInvalidName;
721         break;
722 
723     case AL_INVALID_ENUM:
724         value = alErrInvalidEnum;
725         break;
726 
727     case AL_INVALID_VALUE:
728         value = alErrInvalidValue;
729         break;
730 
731     case AL_INVALID_OPERATION:
732         value = alErrInvalidOp;
733         break;
734 
735     case AL_OUT_OF_MEMORY:
736         value = alErrOutOfMemory;
737         break;
738 
739     default:
740         context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname);
741     }
742     return value;
743 }
744 END_API_FUNC
745 
alDopplerFactor(ALfloat value)746 AL_API void AL_APIENTRY alDopplerFactor(ALfloat value)
747 START_API_FUNC
748 {
749     ContextRef context{GetContextRef()};
750     if UNLIKELY(!context) return;
751 
752     if(!(value >= 0.0f && std::isfinite(value)))
753         context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value);
754     else
755     {
756         std::lock_guard<std::mutex> _{context->mPropLock};
757         context->mDopplerFactor = value;
758         DO_UPDATEPROPS();
759     }
760 }
761 END_API_FUNC
762 
alDopplerVelocity(ALfloat value)763 AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value)
764 START_API_FUNC
765 {
766     ContextRef context{GetContextRef()};
767     if UNLIKELY(!context) return;
768 
769     if(!(value >= 0.0f && std::isfinite(value)))
770         context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value);
771     else
772     {
773         std::lock_guard<std::mutex> _{context->mPropLock};
774         context->mDopplerVelocity = value;
775         DO_UPDATEPROPS();
776     }
777 }
778 END_API_FUNC
779 
alSpeedOfSound(ALfloat value)780 AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value)
781 START_API_FUNC
782 {
783     ContextRef context{GetContextRef()};
784     if UNLIKELY(!context) return;
785 
786     if(!(value > 0.0f && std::isfinite(value)))
787         context->setError(AL_INVALID_VALUE, "Speed of sound %f out of range", value);
788     else
789     {
790         std::lock_guard<std::mutex> _{context->mPropLock};
791         context->mSpeedOfSound = value;
792         DO_UPDATEPROPS();
793     }
794 }
795 END_API_FUNC
796 
alDistanceModel(ALenum value)797 AL_API void AL_APIENTRY alDistanceModel(ALenum value)
798 START_API_FUNC
799 {
800     ContextRef context{GetContextRef()};
801     if UNLIKELY(!context) return;
802 
803     if(auto model = DistanceModelFromALenum(value))
804     {
805         std::lock_guard<std::mutex> _{context->mPropLock};
806         context->mDistanceModel = *model;
807         if(!context->mSourceDistanceModel)
808             DO_UPDATEPROPS();
809     }
810     else
811         context->setError(AL_INVALID_VALUE, "Distance model 0x%04x out of range", value);
812 }
813 END_API_FUNC
814 
815 
alDeferUpdatesSOFT(void)816 AL_API void AL_APIENTRY alDeferUpdatesSOFT(void)
817 START_API_FUNC
818 {
819     ContextRef context{GetContextRef()};
820     if UNLIKELY(!context) return;
821 
822     context->deferUpdates();
823 }
824 END_API_FUNC
825 
alProcessUpdatesSOFT(void)826 AL_API void AL_APIENTRY alProcessUpdatesSOFT(void)
827 START_API_FUNC
828 {
829     ContextRef context{GetContextRef()};
830     if UNLIKELY(!context) return;
831 
832     context->processUpdates();
833 }
834 END_API_FUNC
835 
836 
alGetStringiSOFT(ALenum pname,ALsizei index)837 AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index)
838 START_API_FUNC
839 {
840     ContextRef context{GetContextRef()};
841     if UNLIKELY(!context) return nullptr;
842 
843     const ALchar *value{nullptr};
844     switch(pname)
845     {
846     case AL_RESAMPLER_NAME_SOFT:
847         if(index < 0 || index > static_cast<ALint>(Resampler::Max))
848             context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index);
849         else
850             value = GetResamplerName(static_cast<Resampler>(index));
851         break;
852 
853     default:
854         context->setError(AL_INVALID_VALUE, "Invalid string indexed property");
855     }
856     return value;
857 }
858 END_API_FUNC
859 
860 
UpdateContextProps(ALCcontext * context)861 void UpdateContextProps(ALCcontext *context)
862 {
863     /* Get an unused proprty container, or allocate a new one as needed. */
864     ContextProps *props{context->mFreeContextProps.load(std::memory_order_acquire)};
865     if(!props)
866         props = new ContextProps{};
867     else
868     {
869         ContextProps *next;
870         do {
871             next = props->next.load(std::memory_order_relaxed);
872         } while(context->mFreeContextProps.compare_exchange_weak(props, next,
873                 std::memory_order_seq_cst, std::memory_order_acquire) == 0);
874     }
875 
876     /* Copy in current property values. */
877     props->DopplerFactor = context->mDopplerFactor;
878     props->DopplerVelocity = context->mDopplerVelocity;
879     props->SpeedOfSound = context->mSpeedOfSound;
880 
881     props->SourceDistanceModel = context->mSourceDistanceModel;
882     props->mDistanceModel = context->mDistanceModel;
883 
884     /* Set the new container for updating internal parameters. */
885     props = context->mParams.ContextUpdate.exchange(props, std::memory_order_acq_rel);
886     if(props)
887     {
888         /* If there was an unused update container, put it back in the
889          * freelist.
890          */
891         AtomicReplaceHead(context->mFreeContextProps, props);
892     }
893 }
894