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