Don't call log10 with values too close to 0
[openal-soft.git] / alc / alc.cpp
blobce5bd1becefd4e3e002ca8b98d315f07d6264317
1 /**
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
21 #include "config.h"
23 #include "version.h"
25 #ifdef _WIN32
26 #define WIN32_LEAN_AND_MEAN
27 #include <windows.h>
28 #endif
30 #include <algorithm>
31 #include <array>
32 #include <atomic>
33 #include <bitset>
34 #include <cassert>
35 #include <cctype>
36 #include <chrono>
37 #include <cinttypes>
38 #include <climits>
39 #include <cmath>
40 #include <csignal>
41 #include <cstddef>
42 #include <cstdio>
43 #include <cstdlib>
44 #include <cstring>
45 #include <exception>
46 #include <functional>
47 #include <iterator>
48 #include <limits>
49 #include <memory>
50 #include <mutex>
51 #include <new>
52 #include <optional>
53 #include <stdexcept>
54 #include <string>
55 #include <string_view>
56 #include <tuple>
57 #include <utility>
58 #include <vector>
60 #include "AL/al.h"
61 #include "AL/alc.h"
62 #include "AL/alext.h"
63 #include "AL/efx.h"
65 #include "al/auxeffectslot.h"
66 #include "al/buffer.h"
67 #include "al/debug.h"
68 #include "al/effect.h"
69 #include "al/filter.h"
70 #include "al/source.h"
71 #include "alc/events.h"
72 #include "albit.h"
73 #include "alconfig.h"
74 #include "almalloc.h"
75 #include "alnumbers.h"
76 #include "alnumeric.h"
77 #include "alspan.h"
78 #include "alstring.h"
79 #include "alu.h"
80 #include "atomic.h"
81 #include "context.h"
82 #include "core/ambidefs.h"
83 #include "core/bformatdec.h"
84 #include "core/bs2b.h"
85 #include "core/context.h"
86 #include "core/cpu_caps.h"
87 #include "core/devformat.h"
88 #include "core/device.h"
89 #include "core/effects/base.h"
90 #include "core/effectslot.h"
91 #include "core/filters/nfc.h"
92 #include "core/helpers.h"
93 #include "core/mastering.h"
94 #include "core/fpu_ctrl.h"
95 #include "core/logging.h"
96 #include "core/uhjfilter.h"
97 #include "core/voice.h"
98 #include "core/voice_change.h"
99 #include "device.h"
100 #include "effects/base.h"
101 #include "export_list.h"
102 #include "flexarray.h"
103 #include "inprogext.h"
104 #include "intrusive_ptr.h"
105 #include "opthelpers.h"
106 #include "strutils.h"
108 #include "backends/base.h"
109 #include "backends/null.h"
110 #include "backends/loopback.h"
111 #ifdef HAVE_PIPEWIRE
112 #include "backends/pipewire.h"
113 #endif
114 #ifdef HAVE_JACK
115 #include "backends/jack.h"
116 #endif
117 #ifdef HAVE_PULSEAUDIO
118 #include "backends/pulseaudio.h"
119 #endif
120 #ifdef HAVE_ALSA
121 #include "backends/alsa.h"
122 #endif
123 #ifdef HAVE_WASAPI
124 #include "backends/wasapi.h"
125 #endif
126 #ifdef HAVE_COREAUDIO
127 #include "backends/coreaudio.h"
128 #endif
129 #ifdef HAVE_OPENSL
130 #include "backends/opensl.h"
131 #endif
132 #ifdef HAVE_OBOE
133 #include "backends/oboe.h"
134 #endif
135 #ifdef HAVE_SOLARIS
136 #include "backends/solaris.h"
137 #endif
138 #ifdef HAVE_SNDIO
139 #include "backends/sndio.h"
140 #endif
141 #ifdef HAVE_OSS
142 #include "backends/oss.h"
143 #endif
144 #ifdef HAVE_DSOUND
145 #include "backends/dsound.h"
146 #endif
147 #ifdef HAVE_WINMM
148 #include "backends/winmm.h"
149 #endif
150 #ifdef HAVE_PORTAUDIO
151 #include "backends/portaudio.h"
152 #endif
153 #ifdef HAVE_SDL2
154 #include "backends/sdl2.h"
155 #endif
156 #ifdef HAVE_OTHERIO
157 #include "backends/otherio.h"
158 #endif
159 #ifdef HAVE_WAVE
160 #include "backends/wave.h"
161 #endif
163 #ifdef ALSOFT_EAX
164 #include "al/eax/api.h"
165 #include "al/eax/globals.h"
166 #endif
169 /************************************************
170 * Library initialization
171 ************************************************/
172 #if defined(_WIN32) && !defined(AL_LIBTYPE_STATIC)
173 BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/)
175 switch(reason)
177 case DLL_PROCESS_ATTACH:
178 /* Pin the DLL so we won't get unloaded until the process terminates */
179 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
180 reinterpret_cast<WCHAR*>(module), &module);
181 break;
183 return TRUE;
185 #endif
187 namespace {
189 using namespace std::string_view_literals;
190 using std::chrono::seconds;
191 using std::chrono::nanoseconds;
193 using voidp = void*;
194 using float2 = std::array<float,2>;
197 /************************************************
198 * Backends
199 ************************************************/
200 struct BackendInfo {
201 const char *name;
202 BackendFactory& (*getFactory)();
205 std::array BackendList{
206 #ifdef HAVE_PIPEWIRE
207 BackendInfo{"pipewire", PipeWireBackendFactory::getFactory},
208 #endif
209 #ifdef HAVE_PULSEAUDIO
210 BackendInfo{"pulse", PulseBackendFactory::getFactory},
211 #endif
212 #ifdef HAVE_WASAPI
213 BackendInfo{"wasapi", WasapiBackendFactory::getFactory},
214 #endif
215 #ifdef HAVE_COREAUDIO
216 BackendInfo{"core", CoreAudioBackendFactory::getFactory},
217 #endif
218 #ifdef HAVE_OBOE
219 BackendInfo{"oboe", OboeBackendFactory::getFactory},
220 #endif
221 #ifdef HAVE_OPENSL
222 BackendInfo{"opensl", OSLBackendFactory::getFactory},
223 #endif
224 #ifdef HAVE_ALSA
225 BackendInfo{"alsa", AlsaBackendFactory::getFactory},
226 #endif
227 #ifdef HAVE_SOLARIS
228 BackendInfo{"solaris", SolarisBackendFactory::getFactory},
229 #endif
230 #ifdef HAVE_SNDIO
231 BackendInfo{"sndio", SndIOBackendFactory::getFactory},
232 #endif
233 #ifdef HAVE_OSS
234 BackendInfo{"oss", OSSBackendFactory::getFactory},
235 #endif
236 #ifdef HAVE_JACK
237 BackendInfo{"jack", JackBackendFactory::getFactory},
238 #endif
239 #ifdef HAVE_DSOUND
240 BackendInfo{"dsound", DSoundBackendFactory::getFactory},
241 #endif
242 #ifdef HAVE_WINMM
243 BackendInfo{"winmm", WinMMBackendFactory::getFactory},
244 #endif
245 #ifdef HAVE_PORTAUDIO
246 BackendInfo{"port", PortBackendFactory::getFactory},
247 #endif
248 #ifdef HAVE_SDL2
249 BackendInfo{"sdl2", SDL2BackendFactory::getFactory},
250 #endif
251 #ifdef HAVE_OTHERIO
252 BackendInfo{"otherio", OtherIOBackendFactory::getFactory},
253 #endif
255 BackendInfo{"null", NullBackendFactory::getFactory},
256 #ifdef HAVE_WAVE
257 BackendInfo{"wave", WaveBackendFactory::getFactory},
258 #endif
261 BackendFactory *PlaybackFactory{};
262 BackendFactory *CaptureFactory{};
265 [[nodiscard]] constexpr auto GetNoErrorString() noexcept { return "No Error"; }
266 [[nodiscard]] constexpr auto GetInvalidDeviceString() noexcept { return "Invalid Device"; }
267 [[nodiscard]] constexpr auto GetInvalidContextString() noexcept { return "Invalid Context"; }
268 [[nodiscard]] constexpr auto GetInvalidEnumString() noexcept { return "Invalid Enum"; }
269 [[nodiscard]] constexpr auto GetInvalidValueString() noexcept { return "Invalid Value"; }
270 [[nodiscard]] constexpr auto GetOutOfMemoryString() noexcept { return "Out of Memory"; }
272 [[nodiscard]] constexpr auto GetDefaultName() noexcept { return "OpenAL Soft\0"; }
274 /************************************************
275 * Global variables
276 ************************************************/
278 /* Enumerated device names */
279 std::vector<std::string> alcAllDevicesArray;
280 std::vector<std::string> alcCaptureDeviceArray;
281 std::string alcAllDevicesList;
282 std::string alcCaptureDeviceList;
284 /* Default is always the first in the list */
285 std::string alcDefaultAllDevicesSpecifier;
286 std::string alcCaptureDefaultDeviceSpecifier;
288 std::atomic<ALCenum> LastNullDeviceError{ALC_NO_ERROR};
290 /* Flag to trap ALC device errors */
291 bool TrapALCError{false};
293 /* One-time configuration init control */
294 std::once_flag alc_config_once{};
296 /* Flag to specify if alcSuspendContext/alcProcessContext should defer/process
297 * updates.
299 bool SuspendDefers{true};
301 /* Initial seed for dithering. */
302 constexpr uint DitherRNGSeed{22222u};
305 /************************************************
306 * ALC information
307 ************************************************/
308 [[nodiscard]] constexpr auto GetNoDeviceExtList() noexcept -> std::string_view
310 return "ALC_ENUMERATE_ALL_EXT "
311 "ALC_ENUMERATION_EXT "
312 "ALC_EXT_CAPTURE "
313 "ALC_EXT_direct_context "
314 "ALC_EXT_EFX "
315 "ALC_EXT_thread_local_context "
316 "ALC_SOFT_loopback "
317 "ALC_SOFT_loopback_bformat "
318 "ALC_SOFT_reopen_device "
319 "ALC_SOFT_system_events"sv;
321 [[nodiscard]] constexpr auto GetExtensionList() noexcept -> std::string_view
323 return "ALC_ENUMERATE_ALL_EXT "
324 "ALC_ENUMERATION_EXT "
325 "ALC_EXT_CAPTURE "
326 "ALC_EXT_debug "
327 "ALC_EXT_DEDICATED "
328 "ALC_EXT_direct_context "
329 "ALC_EXT_disconnect "
330 "ALC_EXT_EFX "
331 "ALC_EXT_thread_local_context "
332 "ALC_SOFT_device_clock "
333 "ALC_SOFT_HRTF "
334 "ALC_SOFT_loopback "
335 "ALC_SOFT_loopback_bformat "
336 "ALC_SOFT_output_limiter "
337 "ALC_SOFT_output_mode "
338 "ALC_SOFT_pause_device "
339 "ALC_SOFT_reopen_device "
340 "ALC_SOFT_system_events"sv;
343 constexpr int alcMajorVersion{1};
344 constexpr int alcMinorVersion{1};
346 constexpr int alcEFXMajorVersion{1};
347 constexpr int alcEFXMinorVersion{0};
350 using DeviceRef = al::intrusive_ptr<ALCdevice>;
353 /************************************************
354 * Device lists
355 ************************************************/
356 std::vector<ALCdevice*> DeviceList;
357 std::vector<ALCcontext*> ContextList;
359 std::recursive_mutex ListLock;
362 void alc_initconfig()
364 if(auto loglevel = al::getenv("ALSOFT_LOGLEVEL"))
366 long lvl = strtol(loglevel->c_str(), nullptr, 0);
367 if(lvl >= static_cast<long>(LogLevel::Trace))
368 gLogLevel = LogLevel::Trace;
369 else if(lvl <= static_cast<long>(LogLevel::Disable))
370 gLogLevel = LogLevel::Disable;
371 else
372 gLogLevel = static_cast<LogLevel>(lvl);
375 #ifdef _WIN32
376 if(const auto logfile = al::getenv(L"ALSOFT_LOGFILE"))
378 FILE *logf{_wfopen(logfile->c_str(), L"wt")};
379 if(logf) gLogFile = logf;
380 else
382 auto u8name = wstr_to_utf8(*logfile);
383 ERR("Failed to open log file '%s'\n", u8name.c_str());
386 #else
387 if(const auto logfile = al::getenv("ALSOFT_LOGFILE"))
389 FILE *logf{fopen(logfile->c_str(), "wt")};
390 if(logf) gLogFile = logf;
391 else ERR("Failed to open log file '%s'\n", logfile->c_str());
393 #endif
395 TRACE("Initializing library v%s-%s %s\n", ALSOFT_VERSION, ALSOFT_GIT_COMMIT_HASH,
396 ALSOFT_GIT_BRANCH);
398 std::string names;
399 if(std::size(BackendList) < 1)
400 names = "(none)";
401 else
403 const al::span<const BackendInfo> infos{BackendList};
404 names = infos[0].name;
405 for(const auto &backend : infos.subspan<1>())
407 names += ", ";
408 names += backend.name;
411 TRACE("Supported backends: %s\n", names.c_str());
413 ReadALConfig();
415 if(auto suspendmode = al::getenv("__ALSOFT_SUSPEND_CONTEXT"))
417 if(al::case_compare(*suspendmode, "ignore"sv) == 0)
419 SuspendDefers = false;
420 TRACE("Selected context suspend behavior, \"ignore\"\n");
422 else
423 ERR("Unhandled context suspend behavior setting: \"%s\"\n", suspendmode->c_str());
426 int capfilter{0};
427 #if defined(HAVE_SSE4_1)
428 capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
429 #elif defined(HAVE_SSE3)
430 capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
431 #elif defined(HAVE_SSE2)
432 capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2;
433 #elif defined(HAVE_SSE)
434 capfilter |= CPU_CAP_SSE;
435 #endif
436 #ifdef HAVE_NEON
437 capfilter |= CPU_CAP_NEON;
438 #endif
439 if(auto cpuopt = ConfigValueStr({}, {}, "disable-cpu-exts"sv))
441 std::string_view cpulist{*cpuopt};
442 if(al::case_compare(cpulist, "all"sv) == 0)
443 capfilter = 0;
444 else while(!cpulist.empty())
446 auto nextpos = std::min(cpulist.find(','), cpulist.size());
447 auto entry = cpulist.substr(0, nextpos);
449 while(nextpos < cpulist.size() && cpulist[nextpos] == ',')
450 ++nextpos;
451 cpulist.remove_prefix(nextpos);
453 while(!entry.empty() && std::isspace(entry.front()))
454 entry.remove_prefix(1);
455 while(!entry.empty() && std::isspace(entry.back()))
456 entry.remove_suffix(1);
457 if(entry.empty())
458 continue;
460 if(al::case_compare(entry, "sse"sv) == 0)
461 capfilter &= ~CPU_CAP_SSE;
462 else if(al::case_compare(entry, "sse2"sv) == 0)
463 capfilter &= ~CPU_CAP_SSE2;
464 else if(al::case_compare(entry, "sse3"sv) == 0)
465 capfilter &= ~CPU_CAP_SSE3;
466 else if(al::case_compare(entry, "sse4.1"sv) == 0)
467 capfilter &= ~CPU_CAP_SSE4_1;
468 else if(al::case_compare(entry, "neon"sv) == 0)
469 capfilter &= ~CPU_CAP_NEON;
470 else
471 WARN("Invalid CPU extension \"%.*s\"\n", al::sizei(entry), entry.data());
474 if(auto cpuopt = GetCPUInfo())
476 if(!cpuopt->mVendor.empty() || !cpuopt->mName.empty())
478 TRACE("Vendor ID: \"%s\"\n", cpuopt->mVendor.c_str());
479 TRACE("Name: \"%s\"\n", cpuopt->mName.c_str());
481 const int caps{cpuopt->mCaps};
482 TRACE("Extensions:%s%s%s%s%s%s\n",
483 ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""),
484 ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""),
485 ((capfilter&CPU_CAP_SSE3) ? ((caps&CPU_CAP_SSE3) ? " +SSE3" : " -SSE3") : ""),
486 ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""),
487 ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +NEON" : " -NEON") : ""),
488 ((!capfilter) ? " -none-" : ""));
489 CPUCapFlags = caps & capfilter;
492 if(auto priopt = ConfigValueInt({}, {}, "rt-prio"sv))
493 RTPrioLevel = *priopt;
494 if(auto limopt = ConfigValueBool({}, {}, "rt-time-limit"sv))
495 AllowRTTimeLimit = *limopt;
498 CompatFlagBitset compatflags{};
499 auto checkflag = [](const char *envname, const std::string_view optname) -> bool
501 if(auto optval = al::getenv(envname))
503 return al::case_compare(*optval, "true"sv) == 0
504 || strtol(optval->c_str(), nullptr, 0) == 1;
506 return GetConfigValueBool({}, "game_compat", optname, false);
508 sBufferSubDataCompat = checkflag("__ALSOFT_ENABLE_SUB_DATA_EXT", "enable-sub-data-ext"sv);
509 compatflags.set(CompatFlags::ReverseX, checkflag("__ALSOFT_REVERSE_X", "reverse-x"sv));
510 compatflags.set(CompatFlags::ReverseY, checkflag("__ALSOFT_REVERSE_Y", "reverse-y"sv));
511 compatflags.set(CompatFlags::ReverseZ, checkflag("__ALSOFT_REVERSE_Z", "reverse-z"sv));
513 aluInit(compatflags, ConfigValueFloat({}, "game_compat"sv, "nfc-scale"sv).value_or(1.0f));
515 Voice::InitMixer(ConfigValueStr({}, {}, "resampler"sv));
517 if(auto uhjfiltopt = ConfigValueStr({}, "uhj"sv, "decode-filter"sv))
519 if(al::case_compare(*uhjfiltopt, "fir256"sv) == 0)
520 UhjDecodeQuality = UhjQualityType::FIR256;
521 else if(al::case_compare(*uhjfiltopt, "fir512"sv) == 0)
522 UhjDecodeQuality = UhjQualityType::FIR512;
523 else if(al::case_compare(*uhjfiltopt, "iir"sv) == 0)
524 UhjDecodeQuality = UhjQualityType::IIR;
525 else
526 WARN("Unsupported uhj/decode-filter: %s\n", uhjfiltopt->c_str());
528 if(auto uhjfiltopt = ConfigValueStr({}, "uhj"sv, "encode-filter"sv))
530 if(al::case_compare(*uhjfiltopt, "fir256"sv) == 0)
531 UhjEncodeQuality = UhjQualityType::FIR256;
532 else if(al::case_compare(*uhjfiltopt, "fir512"sv) == 0)
533 UhjEncodeQuality = UhjQualityType::FIR512;
534 else if(al::case_compare(*uhjfiltopt, "iir"sv) == 0)
535 UhjEncodeQuality = UhjQualityType::IIR;
536 else
537 WARN("Unsupported uhj/encode-filter: %s\n", uhjfiltopt->c_str());
540 if(auto traperr = al::getenv("ALSOFT_TRAP_ERROR"); traperr
541 && (al::case_compare(*traperr, "true"sv) == 0
542 || std::strtol(traperr->c_str(), nullptr, 0) == 1))
544 TrapALError = true;
545 TrapALCError = true;
547 else
549 traperr = al::getenv("ALSOFT_TRAP_AL_ERROR");
550 if(traperr)
551 TrapALError = al::case_compare(*traperr, "true"sv) == 0
552 || strtol(traperr->c_str(), nullptr, 0) == 1;
553 else
554 TrapALError = GetConfigValueBool({}, {}, "trap-al-error"sv, false);
556 traperr = al::getenv("ALSOFT_TRAP_ALC_ERROR");
557 if(traperr)
558 TrapALCError = al::case_compare(*traperr, "true"sv) == 0
559 || strtol(traperr->c_str(), nullptr, 0) == 1;
560 else
561 TrapALCError = GetConfigValueBool({}, {}, "trap-alc-error"sv, false);
564 if(auto boostopt = ConfigValueFloat({}, "reverb"sv, "boost"sv))
566 const float valf{std::isfinite(*boostopt) ? std::clamp(*boostopt, -24.0f, 24.0f) : 0.0f};
567 ReverbBoost *= std::pow(10.0f, valf / 20.0f);
570 auto BackendListEnd = BackendList.end();
571 auto devopt = al::getenv("ALSOFT_DRIVERS");
572 if(!devopt) devopt = ConfigValueStr({}, {}, "drivers"sv);
573 if(devopt)
575 auto backendlist_cur = BackendList.begin();
577 bool endlist{true};
578 std::string_view drvlist{*devopt};
579 while(!drvlist.empty())
581 auto nextpos = std::min(drvlist.find(','), drvlist.size());
582 auto entry = drvlist.substr(0, nextpos);
584 endlist = true;
585 if(nextpos < drvlist.size())
587 endlist = false;
588 while(nextpos < drvlist.size() && drvlist[nextpos] == ',')
589 ++nextpos;
591 drvlist.remove_prefix(nextpos);
593 while(!entry.empty() && std::isspace(entry.front()))
594 entry.remove_prefix(1);
595 const bool delitem{!entry.empty() && entry.front() == '-'};
596 if(delitem) entry.remove_prefix(1);
598 while(!entry.empty() && std::isspace(entry.back()))
599 entry.remove_suffix(1);
600 if(entry.empty())
601 continue;
603 #ifdef HAVE_WASAPI
604 /* HACK: For backwards compatibility, convert backend references of
605 * mmdevapi to wasapi. This should eventually be removed.
607 if(entry == "mmdevapi"sv)
608 entry = "wasapi"sv;
609 #endif
611 auto find_backend = [entry](const BackendInfo &backend) -> bool
612 { return entry == backend.name; };
613 auto this_backend = std::find_if(BackendList.begin(), BackendListEnd, find_backend);
615 if(this_backend == BackendListEnd)
616 continue;
618 if(delitem)
619 BackendListEnd = std::move(this_backend+1, BackendListEnd, this_backend);
620 else
621 backendlist_cur = std::rotate(backendlist_cur, this_backend, this_backend+1);
624 if(endlist)
625 BackendListEnd = backendlist_cur;
628 auto init_backend = [](BackendInfo &backend) -> void
630 if(PlaybackFactory && CaptureFactory)
631 return;
633 BackendFactory &factory = backend.getFactory();
634 if(!factory.init())
636 WARN("Failed to initialize backend \"%s\"\n", backend.name);
637 return;
640 TRACE("Initialized backend \"%s\"\n", backend.name);
641 if(!PlaybackFactory && factory.querySupport(BackendType::Playback))
643 PlaybackFactory = &factory;
644 TRACE("Added \"%s\" for playback\n", backend.name);
646 if(!CaptureFactory && factory.querySupport(BackendType::Capture))
648 CaptureFactory = &factory;
649 TRACE("Added \"%s\" for capture\n", backend.name);
652 std::for_each(BackendList.begin(), BackendListEnd, init_backend);
654 LoopbackBackendFactory::getFactory().init();
656 if(!PlaybackFactory)
657 WARN("No playback backend available!\n");
658 if(!CaptureFactory)
659 WARN("No capture backend available!\n");
661 if(auto exclopt = ConfigValueStr({}, {}, "excludefx"sv))
663 std::string_view exclude{*exclopt};
664 while(!exclude.empty())
666 const auto nextpos = exclude.find(',');
667 const auto entry = exclude.substr(0, nextpos);
668 exclude.remove_prefix((nextpos < exclude.size()) ? nextpos+1 : exclude.size());
670 std::for_each(gEffectList.cbegin(), gEffectList.cend(),
671 [entry](const EffectList &effectitem) noexcept
673 if(entry == std::data(effectitem.name))
674 DisabledEffects.set(effectitem.type);
679 InitEffect(&ALCcontext::sDefaultEffect);
680 auto defrevopt = al::getenv("ALSOFT_DEFAULT_REVERB");
681 if(!defrevopt) defrevopt = ConfigValueStr({}, {}, "default-reverb"sv);
682 if(defrevopt) LoadReverbPreset(*defrevopt, &ALCcontext::sDefaultEffect);
684 #ifdef ALSOFT_EAX
686 if(const auto eax_enable_opt = ConfigValueBool({}, "eax", "enable"))
688 eax_g_is_enabled = *eax_enable_opt;
689 if(!eax_g_is_enabled)
690 TRACE("%s\n", "EAX disabled by a configuration.");
692 else
693 eax_g_is_enabled = true;
695 if((DisabledEffects.test(EAXREVERB_EFFECT) || DisabledEffects.test(CHORUS_EFFECT))
696 && eax_g_is_enabled)
698 eax_g_is_enabled = false;
699 TRACE("EAX disabled because %s disabled.\n",
700 (DisabledEffects.test(EAXREVERB_EFFECT) && DisabledEffects.test(CHORUS_EFFECT))
701 ? "EAXReverb and Chorus are" :
702 DisabledEffects.test(EAXREVERB_EFFECT) ? "EAXReverb is" :
703 DisabledEffects.test(CHORUS_EFFECT) ? "Chorus is" : "");
706 #endif // ALSOFT_EAX
708 inline void InitConfig()
709 { std::call_once(alc_config_once, [](){alc_initconfig();}); }
712 /************************************************
713 * Device enumeration
714 ************************************************/
715 void ProbeAllDevicesList()
717 InitConfig();
719 std::lock_guard<std::recursive_mutex> listlock{ListLock};
720 if(!PlaybackFactory)
722 decltype(alcAllDevicesArray){}.swap(alcAllDevicesArray);
723 decltype(alcAllDevicesList){}.swap(alcAllDevicesList);
725 else
727 alcAllDevicesArray = PlaybackFactory->enumerate(BackendType::Playback);
728 decltype(alcAllDevicesList){}.swap(alcAllDevicesList);
729 if(alcAllDevicesArray.empty())
730 alcAllDevicesList += '\0';
731 else for(auto &devname : alcAllDevicesArray)
732 alcAllDevicesList.append(devname) += '\0';
735 void ProbeCaptureDeviceList()
737 InitConfig();
739 std::lock_guard<std::recursive_mutex> listlock{ListLock};
740 if(!CaptureFactory)
742 decltype(alcCaptureDeviceArray){}.swap(alcCaptureDeviceArray);
743 decltype(alcCaptureDeviceList){}.swap(alcCaptureDeviceList);
745 else
747 alcCaptureDeviceArray = CaptureFactory->enumerate(BackendType::Capture);
748 decltype(alcCaptureDeviceList){}.swap(alcCaptureDeviceList);
749 if(alcCaptureDeviceArray.empty())
750 alcCaptureDeviceList += '\0';
751 else for(auto &devname : alcCaptureDeviceArray)
752 alcCaptureDeviceList.append(devname) += '\0';
757 al::span<const ALCint> SpanFromAttributeList(const ALCint *attribs) noexcept
759 al::span<const ALCint> attrSpan;
760 if(attribs)
762 const ALCint *attrEnd{attribs};
763 while(*attrEnd != 0)
764 attrEnd += 2; /* NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) */
765 attrSpan = {attribs, attrEnd};
767 return attrSpan;
770 struct DevFmtPair { DevFmtChannels chans; DevFmtType type; };
771 std::optional<DevFmtPair> DecomposeDevFormat(ALenum format)
773 struct FormatType {
774 ALenum format;
775 DevFmtChannels channels;
776 DevFmtType type;
778 static constexpr std::array list{
779 FormatType{AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte},
780 FormatType{AL_FORMAT_MONO16, DevFmtMono, DevFmtShort},
781 FormatType{AL_FORMAT_MONO_I32, DevFmtMono, DevFmtInt},
782 FormatType{AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat},
784 FormatType{AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte},
785 FormatType{AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort},
786 FormatType{AL_FORMAT_STEREO_I32, DevFmtStereo, DevFmtInt},
787 FormatType{AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat},
789 FormatType{AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte},
790 FormatType{AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort},
791 FormatType{AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat},
792 FormatType{AL_FORMAT_QUAD_I32, DevFmtQuad, DevFmtInt},
793 FormatType{AL_FORMAT_QUAD_FLOAT32, DevFmtQuad, DevFmtFloat},
795 FormatType{AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte},
796 FormatType{AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort},
797 FormatType{AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat},
798 FormatType{AL_FORMAT_51CHN_I32, DevFmtX51, DevFmtInt},
799 FormatType{AL_FORMAT_51CHN_FLOAT32, DevFmtX51, DevFmtFloat},
801 FormatType{AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte},
802 FormatType{AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort},
803 FormatType{AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat},
804 FormatType{AL_FORMAT_61CHN_I32, DevFmtX61, DevFmtInt},
805 FormatType{AL_FORMAT_61CHN_FLOAT32, DevFmtX61, DevFmtFloat},
807 FormatType{AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte},
808 FormatType{AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort},
809 FormatType{AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat},
810 FormatType{AL_FORMAT_71CHN_I32, DevFmtX71, DevFmtInt},
811 FormatType{AL_FORMAT_71CHN_FLOAT32, DevFmtX71, DevFmtFloat},
814 for(const auto &item : list)
816 if(item.format == format)
817 return DevFmtPair{item.channels, item.type};
820 return std::nullopt;
823 std::optional<DevFmtType> DevFmtTypeFromEnum(ALCenum type)
825 switch(type)
827 case ALC_BYTE_SOFT: return DevFmtByte;
828 case ALC_UNSIGNED_BYTE_SOFT: return DevFmtUByte;
829 case ALC_SHORT_SOFT: return DevFmtShort;
830 case ALC_UNSIGNED_SHORT_SOFT: return DevFmtUShort;
831 case ALC_INT_SOFT: return DevFmtInt;
832 case ALC_UNSIGNED_INT_SOFT: return DevFmtUInt;
833 case ALC_FLOAT_SOFT: return DevFmtFloat;
835 WARN("Unsupported format type: 0x%04x\n", type);
836 return std::nullopt;
838 ALCenum EnumFromDevFmt(DevFmtType type)
840 switch(type)
842 case DevFmtByte: return ALC_BYTE_SOFT;
843 case DevFmtUByte: return ALC_UNSIGNED_BYTE_SOFT;
844 case DevFmtShort: return ALC_SHORT_SOFT;
845 case DevFmtUShort: return ALC_UNSIGNED_SHORT_SOFT;
846 case DevFmtInt: return ALC_INT_SOFT;
847 case DevFmtUInt: return ALC_UNSIGNED_INT_SOFT;
848 case DevFmtFloat: return ALC_FLOAT_SOFT;
850 throw std::runtime_error{"Invalid DevFmtType: "+std::to_string(int(type))};
853 std::optional<DevFmtChannels> DevFmtChannelsFromEnum(ALCenum channels)
855 switch(channels)
857 case ALC_MONO_SOFT: return DevFmtMono;
858 case ALC_STEREO_SOFT: return DevFmtStereo;
859 case ALC_QUAD_SOFT: return DevFmtQuad;
860 case ALC_5POINT1_SOFT: return DevFmtX51;
861 case ALC_6POINT1_SOFT: return DevFmtX61;
862 case ALC_7POINT1_SOFT: return DevFmtX71;
863 case ALC_BFORMAT3D_SOFT: return DevFmtAmbi3D;
865 WARN("Unsupported format channels: 0x%04x\n", channels);
866 return std::nullopt;
868 ALCenum EnumFromDevFmt(DevFmtChannels channels)
870 switch(channels)
872 case DevFmtMono: return ALC_MONO_SOFT;
873 case DevFmtStereo: return ALC_STEREO_SOFT;
874 case DevFmtQuad: return ALC_QUAD_SOFT;
875 case DevFmtX51: return ALC_5POINT1_SOFT;
876 case DevFmtX61: return ALC_6POINT1_SOFT;
877 case DevFmtX71: return ALC_7POINT1_SOFT;
878 case DevFmtAmbi3D: return ALC_BFORMAT3D_SOFT;
879 /* FIXME: Shouldn't happen. */
880 case DevFmtX714:
881 case DevFmtX7144:
882 case DevFmtX3D71: break;
884 throw std::runtime_error{"Invalid DevFmtChannels: "+std::to_string(int(channels))};
887 std::optional<DevAmbiLayout> DevAmbiLayoutFromEnum(ALCenum layout)
889 switch(layout)
891 case ALC_FUMA_SOFT: return DevAmbiLayout::FuMa;
892 case ALC_ACN_SOFT: return DevAmbiLayout::ACN;
894 WARN("Unsupported ambisonic layout: 0x%04x\n", layout);
895 return std::nullopt;
897 ALCenum EnumFromDevAmbi(DevAmbiLayout layout)
899 switch(layout)
901 case DevAmbiLayout::FuMa: return ALC_FUMA_SOFT;
902 case DevAmbiLayout::ACN: return ALC_ACN_SOFT;
904 throw std::runtime_error{"Invalid DevAmbiLayout: "+std::to_string(int(layout))};
907 std::optional<DevAmbiScaling> DevAmbiScalingFromEnum(ALCenum scaling)
909 switch(scaling)
911 case ALC_FUMA_SOFT: return DevAmbiScaling::FuMa;
912 case ALC_SN3D_SOFT: return DevAmbiScaling::SN3D;
913 case ALC_N3D_SOFT: return DevAmbiScaling::N3D;
915 WARN("Unsupported ambisonic scaling: 0x%04x\n", scaling);
916 return std::nullopt;
918 ALCenum EnumFromDevAmbi(DevAmbiScaling scaling)
920 switch(scaling)
922 case DevAmbiScaling::FuMa: return ALC_FUMA_SOFT;
923 case DevAmbiScaling::SN3D: return ALC_SN3D_SOFT;
924 case DevAmbiScaling::N3D: return ALC_N3D_SOFT;
926 throw std::runtime_error{"Invalid DevAmbiScaling: "+std::to_string(int(scaling))};
930 /* Downmixing channel arrays, to map a device format's missing channels to
931 * existing ones. Based on what PipeWire does, though simplified.
933 constexpr float inv_sqrt2f{static_cast<float>(1.0 / al::numbers::sqrt2)};
934 constexpr std::array FrontStereo3dB{
935 InputRemixMap::TargetMix{FrontLeft, inv_sqrt2f},
936 InputRemixMap::TargetMix{FrontRight, inv_sqrt2f}
938 constexpr std::array FrontStereo6dB{
939 InputRemixMap::TargetMix{FrontLeft, 0.5f},
940 InputRemixMap::TargetMix{FrontRight, 0.5f}
942 constexpr std::array SideStereo3dB{
943 InputRemixMap::TargetMix{SideLeft, inv_sqrt2f},
944 InputRemixMap::TargetMix{SideRight, inv_sqrt2f}
946 constexpr std::array BackStereo3dB{
947 InputRemixMap::TargetMix{BackLeft, inv_sqrt2f},
948 InputRemixMap::TargetMix{BackRight, inv_sqrt2f}
950 constexpr std::array FrontLeft3dB{InputRemixMap::TargetMix{FrontLeft, inv_sqrt2f}};
951 constexpr std::array FrontRight3dB{InputRemixMap::TargetMix{FrontRight, inv_sqrt2f}};
952 constexpr std::array SideLeft0dB{InputRemixMap::TargetMix{SideLeft, 1.0f}};
953 constexpr std::array SideRight0dB{InputRemixMap::TargetMix{SideRight, 1.0f}};
954 constexpr std::array BackLeft0dB{InputRemixMap::TargetMix{BackLeft, 1.0f}};
955 constexpr std::array BackRight0dB{InputRemixMap::TargetMix{BackRight, 1.0f}};
956 constexpr std::array BackCenter3dB{InputRemixMap::TargetMix{BackCenter, inv_sqrt2f}};
958 constexpr std::array StereoDownmix{
959 InputRemixMap{FrontCenter, FrontStereo3dB},
960 InputRemixMap{SideLeft, FrontLeft3dB},
961 InputRemixMap{SideRight, FrontRight3dB},
962 InputRemixMap{BackLeft, FrontLeft3dB},
963 InputRemixMap{BackRight, FrontRight3dB},
964 InputRemixMap{BackCenter, FrontStereo6dB},
966 constexpr std::array QuadDownmix{
967 InputRemixMap{FrontCenter, FrontStereo3dB},
968 InputRemixMap{SideLeft, BackLeft0dB},
969 InputRemixMap{SideRight, BackRight0dB},
970 InputRemixMap{BackCenter, BackStereo3dB},
972 constexpr std::array X51Downmix{
973 InputRemixMap{BackLeft, SideLeft0dB},
974 InputRemixMap{BackRight, SideRight0dB},
975 InputRemixMap{BackCenter, SideStereo3dB},
977 constexpr std::array X61Downmix{
978 InputRemixMap{BackLeft, BackCenter3dB},
979 InputRemixMap{BackRight, BackCenter3dB},
981 constexpr std::array X71Downmix{
982 InputRemixMap{BackCenter, BackStereo3dB},
986 std::unique_ptr<Compressor> CreateDeviceLimiter(const ALCdevice *device, const float threshold)
988 static constexpr bool AutoKnee{true};
989 static constexpr bool AutoAttack{true};
990 static constexpr bool AutoRelease{true};
991 static constexpr bool AutoPostGain{true};
992 static constexpr bool AutoDeclip{true};
993 static constexpr float LookAheadTime{0.001f};
994 static constexpr float HoldTime{0.002f};
995 static constexpr float PreGainDb{0.0f};
996 static constexpr float PostGainDb{0.0f};
997 static constexpr float Ratio{std::numeric_limits<float>::infinity()};
998 static constexpr float KneeDb{0.0f};
999 static constexpr float AttackTime{0.02f};
1000 static constexpr float ReleaseTime{0.2f};
1002 return Compressor::Create(device->RealOut.Buffer.size(), static_cast<float>(device->Frequency),
1003 AutoKnee, AutoAttack, AutoRelease, AutoPostGain, AutoDeclip, LookAheadTime, HoldTime,
1004 PreGainDb, PostGainDb, threshold, Ratio, KneeDb, AttackTime, ReleaseTime);
1008 * Updates the device's base clock time with however many samples have been
1009 * done. This is used so frequency changes on the device don't cause the time
1010 * to jump forward or back. Must not be called while the device is running/
1011 * mixing.
1013 inline void UpdateClockBase(ALCdevice *device)
1015 const auto mixLock = device->getWriteMixLock();
1017 auto samplesDone = device->mSamplesDone.load(std::memory_order_relaxed);
1018 auto clockBase = device->mClockBase.load(std::memory_order_relaxed);
1020 clockBase += nanoseconds{seconds{samplesDone}} / device->Frequency;
1021 device->mClockBase.store(clockBase, std::memory_order_relaxed);
1022 device->mSamplesDone.store(0, std::memory_order_relaxed);
1026 * Updates device parameters according to the attribute list (caller is
1027 * responsible for holding the list lock).
1029 ALCenum UpdateDeviceParams(ALCdevice *device, const al::span<const int> attrList)
1031 if(attrList.empty() && device->Type == DeviceType::Loopback)
1033 WARN("Missing attributes for loopback device\n");
1034 return ALC_INVALID_VALUE;
1037 uint numMono{device->NumMonoSources};
1038 uint numStereo{device->NumStereoSources};
1039 uint numSends{device->NumAuxSends};
1040 std::optional<StereoEncoding> stereomode;
1041 std::optional<bool> optlimit;
1042 std::optional<uint> optsrate;
1043 std::optional<DevFmtChannels> optchans;
1044 std::optional<DevFmtType> opttype;
1045 std::optional<DevAmbiLayout> optlayout;
1046 std::optional<DevAmbiScaling> optscale;
1047 uint period_size{DefaultUpdateSize};
1048 uint buffer_size{DefaultUpdateSize * DefaultNumUpdates};
1049 int hrtf_id{-1};
1050 uint aorder{0u};
1052 if(device->Type != DeviceType::Loopback)
1054 /* Get default settings from the user configuration */
1056 if(auto freqopt = device->configValue<uint>({}, "frequency"))
1058 optsrate = std::clamp<uint>(*freqopt, MinOutputRate, MaxOutputRate);
1060 const double scale{static_cast<double>(*optsrate) / double{DefaultOutputRate}};
1061 period_size = static_cast<uint>(std::lround(period_size * scale));
1064 if(auto persizeopt = device->configValue<uint>({}, "period_size"))
1065 period_size = std::clamp(*persizeopt, 64u, 8192u);
1066 if(auto numperopt = device->configValue<uint>({}, "periods"))
1067 buffer_size = std::clamp(*numperopt, 2u, 16u) * period_size;
1068 else
1069 buffer_size = period_size * uint{DefaultNumUpdates};
1071 if(auto typeopt = device->configValue<std::string>({}, "sample-type"))
1073 struct TypeMap {
1074 std::string_view name;
1075 DevFmtType type;
1077 constexpr std::array typelist{
1078 TypeMap{"int8"sv, DevFmtByte },
1079 TypeMap{"uint8"sv, DevFmtUByte },
1080 TypeMap{"int16"sv, DevFmtShort },
1081 TypeMap{"uint16"sv, DevFmtUShort},
1082 TypeMap{"int32"sv, DevFmtInt },
1083 TypeMap{"uint32"sv, DevFmtUInt },
1084 TypeMap{"float32"sv, DevFmtFloat },
1087 const ALCchar *fmt{typeopt->c_str()};
1088 auto iter = std::find_if(typelist.begin(), typelist.end(),
1089 [svfmt=std::string_view{fmt}](const TypeMap &entry) -> bool
1090 { return al::case_compare(entry.name, svfmt) == 0; });
1091 if(iter == typelist.end())
1092 ERR("Unsupported sample-type: %s\n", fmt);
1093 else
1094 opttype = iter->type;
1096 if(auto chanopt = device->configValue<std::string>({}, "channels"))
1098 struct ChannelMap {
1099 std::string_view name;
1100 DevFmtChannels chans;
1101 uint8_t order;
1103 constexpr std::array chanlist{
1104 ChannelMap{"mono"sv, DevFmtMono, 0},
1105 ChannelMap{"stereo"sv, DevFmtStereo, 0},
1106 ChannelMap{"quad"sv, DevFmtQuad, 0},
1107 ChannelMap{"surround51"sv, DevFmtX51, 0},
1108 ChannelMap{"surround61"sv, DevFmtX61, 0},
1109 ChannelMap{"surround71"sv, DevFmtX71, 0},
1110 ChannelMap{"surround714"sv, DevFmtX714, 0},
1111 ChannelMap{"surround7144"sv, DevFmtX7144, 0},
1112 ChannelMap{"surround3d71"sv, DevFmtX3D71, 0},
1113 ChannelMap{"surround51rear"sv, DevFmtX51, 0},
1114 ChannelMap{"ambi1"sv, DevFmtAmbi3D, 1},
1115 ChannelMap{"ambi2"sv, DevFmtAmbi3D, 2},
1116 ChannelMap{"ambi3"sv, DevFmtAmbi3D, 3},
1119 const ALCchar *fmt{chanopt->c_str()};
1120 auto iter = std::find_if(chanlist.begin(), chanlist.end(),
1121 [svfmt=std::string_view{fmt}](const ChannelMap &entry) -> bool
1122 { return al::case_compare(entry.name, svfmt) == 0; });
1123 if(iter == chanlist.end())
1124 ERR("Unsupported channels: %s\n", fmt);
1125 else
1127 optchans = iter->chans;
1128 aorder = iter->order;
1131 if(auto ambiopt = device->configValue<std::string>({}, "ambi-format"sv))
1133 if(al::case_compare(*ambiopt, "fuma"sv) == 0)
1135 optlayout = DevAmbiLayout::FuMa;
1136 optscale = DevAmbiScaling::FuMa;
1138 else if(al::case_compare(*ambiopt, "acn+fuma"sv) == 0)
1140 optlayout = DevAmbiLayout::ACN;
1141 optscale = DevAmbiScaling::FuMa;
1143 else if(al::case_compare(*ambiopt, "ambix"sv) == 0
1144 || al::case_compare(*ambiopt, "acn+sn3d"sv) == 0)
1146 optlayout = DevAmbiLayout::ACN;
1147 optscale = DevAmbiScaling::SN3D;
1149 else if(al::case_compare(*ambiopt, "acn+n3d"sv) == 0)
1151 optlayout = DevAmbiLayout::ACN;
1152 optscale = DevAmbiScaling::N3D;
1154 else
1155 ERR("Unsupported ambi-format: %s\n", ambiopt->c_str());
1158 if(auto hrtfopt = device->configValue<std::string>({}, "hrtf"sv))
1160 WARN("general/hrtf is deprecated, please use stereo-encoding instead\n");
1162 if(al::case_compare(*hrtfopt, "true"sv) == 0)
1163 stereomode = StereoEncoding::Hrtf;
1164 else if(al::case_compare(*hrtfopt, "false"sv) == 0)
1166 if(!stereomode || *stereomode == StereoEncoding::Hrtf)
1167 stereomode = StereoEncoding::Default;
1169 else if(al::case_compare(*hrtfopt, "auto"sv) != 0)
1170 ERR("Unexpected hrtf value: %s\n", hrtfopt->c_str());
1174 if(auto encopt = device->configValue<std::string>({}, "stereo-encoding"sv))
1176 if(al::case_compare(*encopt, "basic"sv) == 0 || al::case_compare(*encopt, "panpot"sv) == 0)
1177 stereomode = StereoEncoding::Basic;
1178 else if(al::case_compare(*encopt, "uhj") == 0)
1179 stereomode = StereoEncoding::Uhj;
1180 else if(al::case_compare(*encopt, "hrtf") == 0)
1181 stereomode = StereoEncoding::Hrtf;
1182 else
1183 ERR("Unexpected stereo-encoding: %s\n", encopt->c_str());
1186 // Check for app-specified attributes
1187 if(!attrList.empty())
1189 ALenum outmode{ALC_ANY_SOFT};
1190 std::optional<bool> opthrtf;
1191 int freqAttr{};
1193 #define ATTRIBUTE(a) a: TRACE("%s = %d\n", #a, attrList[attrIdx + 1]);
1194 for(size_t attrIdx{0};attrIdx < attrList.size();attrIdx+=2)
1196 switch(attrList[attrIdx])
1198 case ATTRIBUTE(ALC_FORMAT_CHANNELS_SOFT)
1199 if(device->Type == DeviceType::Loopback)
1200 optchans = DevFmtChannelsFromEnum(attrList[attrIdx + 1]);
1201 break;
1203 case ATTRIBUTE(ALC_FORMAT_TYPE_SOFT)
1204 if(device->Type == DeviceType::Loopback)
1205 opttype = DevFmtTypeFromEnum(attrList[attrIdx + 1]);
1206 break;
1208 case ATTRIBUTE(ALC_FREQUENCY)
1209 freqAttr = attrList[attrIdx + 1];
1210 break;
1212 case ATTRIBUTE(ALC_AMBISONIC_LAYOUT_SOFT)
1213 if(device->Type == DeviceType::Loopback)
1214 optlayout = DevAmbiLayoutFromEnum(attrList[attrIdx + 1]);
1215 break;
1217 case ATTRIBUTE(ALC_AMBISONIC_SCALING_SOFT)
1218 if(device->Type == DeviceType::Loopback)
1219 optscale = DevAmbiScalingFromEnum(attrList[attrIdx + 1]);
1220 break;
1222 case ATTRIBUTE(ALC_AMBISONIC_ORDER_SOFT)
1223 if(device->Type == DeviceType::Loopback)
1224 aorder = static_cast<uint>(attrList[attrIdx + 1]);
1225 break;
1227 case ATTRIBUTE(ALC_MONO_SOURCES)
1228 numMono = static_cast<uint>(attrList[attrIdx + 1]);
1229 if(numMono > INT_MAX) numMono = 0;
1230 break;
1232 case ATTRIBUTE(ALC_STEREO_SOURCES)
1233 numStereo = static_cast<uint>(attrList[attrIdx + 1]);
1234 if(numStereo > INT_MAX) numStereo = 0;
1235 break;
1237 case ATTRIBUTE(ALC_MAX_AUXILIARY_SENDS)
1238 numSends = static_cast<uint>(attrList[attrIdx + 1]);
1239 if(numSends > uint{std::numeric_limits<int>::max()}) numSends = 0;
1240 else numSends = std::min(numSends, uint{MaxSendCount});
1241 break;
1243 case ATTRIBUTE(ALC_HRTF_SOFT)
1244 if(attrList[attrIdx + 1] == ALC_FALSE)
1245 opthrtf = false;
1246 else if(attrList[attrIdx + 1] == ALC_TRUE)
1247 opthrtf = true;
1248 else if(attrList[attrIdx + 1] == ALC_DONT_CARE_SOFT)
1249 opthrtf = std::nullopt;
1250 break;
1252 case ATTRIBUTE(ALC_HRTF_ID_SOFT)
1253 hrtf_id = attrList[attrIdx + 1];
1254 break;
1256 case ATTRIBUTE(ALC_OUTPUT_LIMITER_SOFT)
1257 if(attrList[attrIdx + 1] == ALC_FALSE)
1258 optlimit = false;
1259 else if(attrList[attrIdx + 1] == ALC_TRUE)
1260 optlimit = true;
1261 else if(attrList[attrIdx + 1] == ALC_DONT_CARE_SOFT)
1262 optlimit = std::nullopt;
1263 break;
1265 case ATTRIBUTE(ALC_OUTPUT_MODE_SOFT)
1266 outmode = attrList[attrIdx + 1];
1267 break;
1269 default:
1270 TRACE("0x%04X = %d (0x%x)\n", attrList[attrIdx],
1271 attrList[attrIdx + 1], attrList[attrIdx + 1]);
1272 break;
1275 #undef ATTRIBUTE
1277 if(device->Type == DeviceType::Loopback)
1279 if(!optchans || !opttype)
1280 return ALC_INVALID_VALUE;
1281 if(freqAttr < int{MinOutputRate} || freqAttr > int{MaxOutputRate})
1282 return ALC_INVALID_VALUE;
1283 if(*optchans == DevFmtAmbi3D)
1285 if(!optlayout || !optscale)
1286 return ALC_INVALID_VALUE;
1287 if(aorder < 1 || aorder > MaxAmbiOrder)
1288 return ALC_INVALID_VALUE;
1289 if((*optlayout == DevAmbiLayout::FuMa || *optscale == DevAmbiScaling::FuMa)
1290 && aorder > 3)
1291 return ALC_INVALID_VALUE;
1293 else if(*optchans == DevFmtStereo)
1295 if(opthrtf)
1297 if(*opthrtf)
1298 stereomode = StereoEncoding::Hrtf;
1299 else
1301 if(stereomode.value_or(StereoEncoding::Hrtf) == StereoEncoding::Hrtf)
1302 stereomode = StereoEncoding::Default;
1306 if(outmode == ALC_STEREO_BASIC_SOFT)
1307 stereomode = StereoEncoding::Basic;
1308 else if(outmode == ALC_STEREO_UHJ_SOFT)
1309 stereomode = StereoEncoding::Uhj;
1310 else if(outmode == ALC_STEREO_HRTF_SOFT)
1311 stereomode = StereoEncoding::Hrtf;
1314 optsrate = static_cast<uint>(freqAttr);
1316 else
1318 if(opthrtf)
1320 if(*opthrtf)
1321 stereomode = StereoEncoding::Hrtf;
1322 else
1324 if(stereomode.value_or(StereoEncoding::Hrtf) == StereoEncoding::Hrtf)
1325 stereomode = StereoEncoding::Default;
1329 if(outmode != ALC_ANY_SOFT)
1331 using OutputMode = ALCdevice::OutputMode;
1332 switch(OutputMode(outmode))
1334 case OutputMode::Any: break;
1335 case OutputMode::Mono: optchans = DevFmtMono; break;
1336 case OutputMode::Stereo: optchans = DevFmtStereo; break;
1337 case OutputMode::StereoBasic:
1338 optchans = DevFmtStereo;
1339 stereomode = StereoEncoding::Basic;
1340 break;
1341 case OutputMode::Uhj2:
1342 optchans = DevFmtStereo;
1343 stereomode = StereoEncoding::Uhj;
1344 break;
1345 case OutputMode::Hrtf:
1346 optchans = DevFmtStereo;
1347 stereomode = StereoEncoding::Hrtf;
1348 break;
1349 case OutputMode::Quad: optchans = DevFmtQuad; break;
1350 case OutputMode::X51: optchans = DevFmtX51; break;
1351 case OutputMode::X61: optchans = DevFmtX61; break;
1352 case OutputMode::X71: optchans = DevFmtX71; break;
1356 if(freqAttr)
1358 uint oldrate = optsrate.value_or(DefaultOutputRate);
1359 freqAttr = std::clamp<int>(freqAttr, MinOutputRate, MaxOutputRate);
1361 const double scale{static_cast<double>(freqAttr) / oldrate};
1362 period_size = static_cast<uint>(std::lround(period_size * scale));
1363 buffer_size = static_cast<uint>(std::lround(buffer_size * scale));
1364 optsrate = static_cast<uint>(freqAttr);
1368 /* If a context is already running on the device, stop playback so the
1369 * device attributes can be updated.
1371 if(device->mDeviceState == DeviceState::Playing)
1373 device->Backend->stop();
1374 device->mDeviceState = DeviceState::Unprepared;
1377 UpdateClockBase(device);
1380 if(device->mDeviceState == DeviceState::Playing)
1381 return ALC_NO_ERROR;
1383 device->mDeviceState = DeviceState::Unprepared;
1384 device->AvgSpeakerDist = 0.0f;
1385 device->mNFCtrlFilter = NfcFilter{};
1386 device->mUhjEncoder = nullptr;
1387 device->AmbiDecoder = nullptr;
1388 device->Bs2b = nullptr;
1389 device->PostProcess = nullptr;
1391 device->Limiter = nullptr;
1392 device->ChannelDelays = nullptr;
1394 std::fill(std::begin(device->HrtfAccumData), std::end(device->HrtfAccumData), float2{});
1396 device->Dry.AmbiMap.fill(BFChannelConfig{});
1397 device->Dry.Buffer = {};
1398 std::fill(std::begin(device->NumChannelsPerOrder), std::end(device->NumChannelsPerOrder), 0u);
1399 device->RealOut.RemixMap = {};
1400 device->RealOut.ChannelIndex.fill(InvalidChannelIndex);
1401 device->RealOut.Buffer = {};
1402 device->MixBuffer.clear();
1403 device->MixBuffer.shrink_to_fit();
1405 UpdateClockBase(device);
1406 device->FixedLatency = nanoseconds::zero();
1408 device->DitherDepth = 0.0f;
1409 device->DitherSeed = DitherRNGSeed;
1411 device->mHrtfStatus = ALC_HRTF_DISABLED_SOFT;
1413 /*************************************************************************
1414 * Update device format request
1417 if(device->Type == DeviceType::Loopback)
1419 device->Frequency = *optsrate;
1420 device->FmtChans = *optchans;
1421 device->FmtType = *opttype;
1422 if(device->FmtChans == DevFmtAmbi3D)
1424 device->mAmbiOrder = aorder;
1425 device->mAmbiLayout = *optlayout;
1426 device->mAmbiScale = *optscale;
1428 device->Flags.set(FrequencyRequest).set(ChannelsRequest).set(SampleTypeRequest);
1430 else
1432 device->FmtType = opttype.value_or(DevFmtTypeDefault);
1433 device->FmtChans = optchans.value_or(DevFmtChannelsDefault);
1434 device->mAmbiOrder = 0;
1435 device->BufferSize = buffer_size;
1436 device->UpdateSize = period_size;
1437 device->Frequency = optsrate.value_or(DefaultOutputRate);
1438 device->Flags.set(FrequencyRequest, optsrate.has_value())
1439 .set(ChannelsRequest, optchans.has_value())
1440 .set(SampleTypeRequest, opttype.has_value());
1442 if(device->FmtChans == DevFmtAmbi3D)
1444 device->mAmbiOrder = std::clamp(aorder, 1u, uint{MaxAmbiOrder});
1445 device->mAmbiLayout = optlayout.value_or(DevAmbiLayout::Default);
1446 device->mAmbiScale = optscale.value_or(DevAmbiScaling::Default);
1447 if(device->mAmbiOrder > 3
1448 && (device->mAmbiLayout == DevAmbiLayout::FuMa
1449 || device->mAmbiScale == DevAmbiScaling::FuMa))
1451 ERR("FuMa is incompatible with %d%s order ambisonics (up to 3rd order only)\n",
1452 device->mAmbiOrder, GetCounterSuffix(device->mAmbiOrder));
1453 device->mAmbiOrder = 3;
1458 TRACE("Pre-reset: %s%s, %s%s, %s%uhz, %u / %u buffer\n",
1459 device->Flags.test(ChannelsRequest)?"*":"", DevFmtChannelsString(device->FmtChans),
1460 device->Flags.test(SampleTypeRequest)?"*":"", DevFmtTypeString(device->FmtType),
1461 device->Flags.test(FrequencyRequest)?"*":"", device->Frequency,
1462 device->UpdateSize, device->BufferSize);
1464 const uint oldFreq{device->Frequency};
1465 const DevFmtChannels oldChans{device->FmtChans};
1466 const DevFmtType oldType{device->FmtType};
1467 try {
1468 auto backend = device->Backend.get();
1469 if(!backend->reset())
1470 throw al::backend_exception{al::backend_error::DeviceError, "Device reset failure"};
1472 catch(std::exception &e) {
1473 ERR("Device error: %s\n", e.what());
1474 device->handleDisconnect("%s", e.what());
1475 return ALC_INVALID_DEVICE;
1478 if(device->FmtChans != oldChans && device->Flags.test(ChannelsRequest))
1480 ERR("Failed to set %s, got %s instead\n", DevFmtChannelsString(oldChans),
1481 DevFmtChannelsString(device->FmtChans));
1482 device->Flags.reset(ChannelsRequest);
1484 if(device->FmtType != oldType && device->Flags.test(SampleTypeRequest))
1486 ERR("Failed to set %s, got %s instead\n", DevFmtTypeString(oldType),
1487 DevFmtTypeString(device->FmtType));
1488 device->Flags.reset(SampleTypeRequest);
1490 if(device->Frequency != oldFreq && device->Flags.test(FrequencyRequest))
1492 WARN("Failed to set %uhz, got %uhz instead\n", oldFreq, device->Frequency);
1493 device->Flags.reset(FrequencyRequest);
1496 TRACE("Post-reset: %s, %s, %uhz, %u / %u buffer\n",
1497 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
1498 device->Frequency, device->UpdateSize, device->BufferSize);
1500 if(device->Type != DeviceType::Loopback)
1502 if(auto modeopt = device->configValue<std::string>({}, "stereo-mode"))
1504 if(al::case_compare(*modeopt, "headphones"sv) == 0)
1505 device->Flags.set(DirectEar);
1506 else if(al::case_compare(*modeopt, "speakers"sv) == 0)
1507 device->Flags.reset(DirectEar);
1508 else if(al::case_compare(*modeopt, "auto"sv) != 0)
1509 ERR("Unexpected stereo-mode: %s\n", modeopt->c_str());
1513 aluInitRenderer(device, hrtf_id, stereomode);
1515 /* Calculate the max number of sources, and split them between the mono and
1516 * stereo count given the requested number of stereo sources.
1518 if(auto srcsopt = device->configValue<uint>({}, "sources"sv))
1520 if(*srcsopt <= 0) numMono = 256;
1521 else numMono = std::max(*srcsopt, 16u);
1523 else
1525 numMono = std::min(numMono, std::numeric_limits<int>::max()-numStereo);
1526 numMono = std::max(numMono+numStereo, 256u);
1528 numStereo = std::min(numStereo, numMono);
1529 numMono -= numStereo;
1530 device->SourcesMax = numMono + numStereo;
1531 device->NumMonoSources = numMono;
1532 device->NumStereoSources = numStereo;
1534 if(auto sendsopt = device->configValue<uint>({}, "sends"sv))
1535 numSends = std::min(numSends, std::clamp(*sendsopt, 0u, uint{MaxSendCount}));
1536 device->NumAuxSends = numSends;
1538 TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n",
1539 device->SourcesMax, device->NumMonoSources, device->NumStereoSources,
1540 device->AuxiliaryEffectSlotMax, device->NumAuxSends);
1542 switch(device->FmtChans)
1544 case DevFmtMono: break;
1545 case DevFmtStereo:
1546 if(!device->mUhjEncoder)
1547 device->RealOut.RemixMap = StereoDownmix;
1548 break;
1549 case DevFmtQuad: device->RealOut.RemixMap = QuadDownmix; break;
1550 case DevFmtX51: device->RealOut.RemixMap = X51Downmix; break;
1551 case DevFmtX61: device->RealOut.RemixMap = X61Downmix; break;
1552 case DevFmtX71: device->RealOut.RemixMap = X71Downmix; break;
1553 case DevFmtX714: device->RealOut.RemixMap = X71Downmix; break;
1554 case DevFmtX7144: device->RealOut.RemixMap = X71Downmix; break;
1555 case DevFmtX3D71: device->RealOut.RemixMap = X51Downmix; break;
1556 case DevFmtAmbi3D: break;
1559 size_t sample_delay{0};
1560 if(auto *encoder{device->mUhjEncoder.get()})
1561 sample_delay += encoder->getDelay();
1563 if(device->getConfigValueBool({}, "dither"sv, true))
1565 int depth{device->configValue<int>({}, "dither-depth"sv).value_or(0)};
1566 if(depth <= 0)
1568 switch(device->FmtType)
1570 case DevFmtByte:
1571 case DevFmtUByte:
1572 depth = 8;
1573 break;
1574 case DevFmtShort:
1575 case DevFmtUShort:
1576 depth = 16;
1577 break;
1578 case DevFmtInt:
1579 case DevFmtUInt:
1580 case DevFmtFloat:
1581 break;
1585 if(depth > 0)
1587 depth = std::clamp(depth, 2, 24);
1588 device->DitherDepth = std::pow(2.0f, static_cast<float>(depth-1));
1591 if(!(device->DitherDepth > 0.0f))
1592 TRACE("Dithering disabled\n");
1593 else
1594 TRACE("Dithering enabled (%d-bit, %g)\n", float2int(std::log2(device->DitherDepth)+0.5f)+1,
1595 device->DitherDepth);
1597 if(!optlimit)
1598 optlimit = device->configValue<bool>({}, "output-limiter");
1600 /* If the gain limiter is unset, use the limiter for integer-based output
1601 * (where samples must be clamped), and don't for floating-point (which can
1602 * take unclamped samples).
1604 if(!optlimit)
1606 switch(device->FmtType)
1608 case DevFmtByte:
1609 case DevFmtUByte:
1610 case DevFmtShort:
1611 case DevFmtUShort:
1612 case DevFmtInt:
1613 case DevFmtUInt:
1614 optlimit = true;
1615 break;
1616 case DevFmtFloat:
1617 break;
1620 if(!optlimit.value_or(false))
1621 TRACE("Output limiter disabled\n");
1622 else
1624 float thrshld{1.0f};
1625 switch(device->FmtType)
1627 case DevFmtByte:
1628 case DevFmtUByte:
1629 thrshld = 127.0f / 128.0f;
1630 break;
1631 case DevFmtShort:
1632 case DevFmtUShort:
1633 thrshld = 32767.0f / 32768.0f;
1634 break;
1635 case DevFmtInt:
1636 case DevFmtUInt:
1637 case DevFmtFloat:
1638 break;
1640 if(device->DitherDepth > 0.0f)
1641 thrshld -= 1.0f / device->DitherDepth;
1643 const float thrshld_dB{std::log10(thrshld) * 20.0f};
1644 auto limiter = CreateDeviceLimiter(device, thrshld_dB);
1646 sample_delay += limiter->getLookAhead();
1647 device->Limiter = std::move(limiter);
1648 TRACE("Output limiter enabled, %.4fdB limit\n", thrshld_dB);
1651 /* Convert the sample delay from samples to nanosamples to nanoseconds. */
1652 sample_delay = std::min<size_t>(sample_delay, std::numeric_limits<int>::max());
1653 device->FixedLatency += nanoseconds{seconds{sample_delay}} / device->Frequency;
1654 TRACE("Fixed device latency: %" PRId64 "ns\n", int64_t{device->FixedLatency.count()});
1656 FPUCtl mixer_mode{};
1657 auto reset_context = [device](ContextBase *ctxbase)
1659 auto *context = static_cast<ALCcontext*>(ctxbase);
1661 std::unique_lock<std::mutex> proplock{context->mPropLock};
1662 std::unique_lock<std::mutex> slotlock{context->mEffectSlotLock};
1664 /* Clear out unused effect slot clusters. */
1665 auto slot_cluster_not_in_use = [](ContextBase::EffectSlotCluster &clusterptr) -> bool
1667 return std::none_of(clusterptr->begin(), clusterptr->end(),
1668 std::mem_fn(&EffectSlot::InUse));
1670 auto slotcluster_end = std::remove_if(context->mEffectSlotClusters.begin(),
1671 context->mEffectSlotClusters.end(), slot_cluster_not_in_use);
1672 context->mEffectSlotClusters.erase(slotcluster_end, context->mEffectSlotClusters.end());
1674 /* Free all wet buffers. Any in use will be reallocated with an updated
1675 * configuration in aluInitEffectPanning.
1677 auto clear_wetbuffers = [](ContextBase::EffectSlotCluster &clusterptr)
1679 auto clear_buffer = [](EffectSlot &slot)
1681 slot.mWetBuffer.clear();
1682 slot.mWetBuffer.shrink_to_fit();
1683 slot.Wet.Buffer = {};
1685 std::for_each(clusterptr->begin(), clusterptr->end(), clear_buffer);
1687 std::for_each(context->mEffectSlotClusters.begin(), context->mEffectSlotClusters.end(),
1688 clear_wetbuffers);
1690 if(ALeffectslot *slot{context->mDefaultSlot.get()})
1692 auto *slotbase = slot->mSlot;
1693 aluInitEffectPanning(slotbase, context);
1695 if(auto *props = slotbase->Update.exchange(nullptr, std::memory_order_relaxed))
1696 AtomicReplaceHead(context->mFreeEffectSlotProps, props);
1698 EffectState *state{slot->Effect.State.get()};
1699 state->mOutTarget = device->Dry.Buffer;
1700 state->deviceUpdate(device, slot->Buffer);
1701 slot->mPropsDirty = true;
1704 if(EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_relaxed)})
1705 std::fill(curarray->begin()+ptrdiff_t(curarray->size()>>1), curarray->end(), nullptr);
1706 auto reset_slots = [device,context](EffectSlotSubList &sublist)
1708 uint64_t usemask{~sublist.FreeMask};
1709 while(usemask)
1711 const auto idx = static_cast<uint>(al::countr_zero(usemask));
1712 auto &slot = (*sublist.EffectSlots)[idx];
1713 usemask &= ~(1_u64 << idx);
1715 auto *slotbase = slot.mSlot;
1716 aluInitEffectPanning(slotbase, context);
1718 if(auto *props = slotbase->Update.exchange(nullptr, std::memory_order_relaxed))
1719 AtomicReplaceHead(context->mFreeEffectSlotProps, props);
1721 EffectState *state{slot.Effect.State.get()};
1722 state->mOutTarget = device->Dry.Buffer;
1723 state->deviceUpdate(device, slot.Buffer);
1724 slot.mPropsDirty = true;
1727 std::for_each(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
1728 reset_slots);
1730 /* Clear all effect slot props to let them get allocated again. */
1731 context->mEffectSlotPropClusters.clear();
1732 context->mFreeEffectSlotProps.store(nullptr, std::memory_order_relaxed);
1733 slotlock.unlock();
1735 std::unique_lock<std::mutex> srclock{context->mSourceLock};
1736 const uint num_sends{device->NumAuxSends};
1737 auto reset_sources = [num_sends](SourceSubList &sublist)
1739 uint64_t usemask{~sublist.FreeMask};
1740 while(usemask)
1742 const auto idx = static_cast<uint>(al::countr_zero(usemask));
1743 auto &source = (*sublist.Sources)[idx];
1744 usemask &= ~(1_u64 << idx);
1746 auto clear_send = [](ALsource::SendData &send) -> void
1748 if(send.Slot)
1749 DecrementRef(send.Slot->ref);
1750 send.Slot = nullptr;
1751 send.Gain = 1.0f;
1752 send.GainHF = 1.0f;
1753 send.HFReference = LowPassFreqRef;
1754 send.GainLF = 1.0f;
1755 send.LFReference = HighPassFreqRef;
1757 const auto sends = al::span{source.Send}.subspan(num_sends);
1758 std::for_each(sends.begin(), sends.end(), clear_send);
1760 source.mPropsDirty = true;
1763 std::for_each(context->mSourceList.begin(), context->mSourceList.end(), reset_sources);
1765 auto reset_voice = [device,num_sends,context](Voice *voice)
1767 /* Clear extraneous property set sends. */
1768 const auto sendparams = al::span{voice->mProps.Send}.subspan(num_sends);
1769 std::fill(sendparams.begin(), sendparams.end(), VoiceProps::SendData{});
1771 std::fill(voice->mSend.begin()+num_sends, voice->mSend.end(), Voice::TargetData{});
1772 auto clear_wetparams = [num_sends](Voice::ChannelData &chandata)
1774 const auto wetparams = al::span{chandata.mWetParams}.subspan(num_sends);
1775 std::fill(wetparams.begin(), wetparams.end(), SendParams{});
1777 std::for_each(voice->mChans.begin(), voice->mChans.end(), clear_wetparams);
1779 if(VoicePropsItem *props{voice->mUpdate.exchange(nullptr, std::memory_order_relaxed)})
1780 AtomicReplaceHead(context->mFreeVoiceProps, props);
1782 /* Force the voice to stopped if it was stopping. */
1783 Voice::State vstate{Voice::Stopping};
1784 voice->mPlayState.compare_exchange_strong(vstate, Voice::Stopped,
1785 std::memory_order_acquire, std::memory_order_acquire);
1786 if(voice->mSourceID.load(std::memory_order_relaxed) == 0u)
1787 return;
1789 voice->prepare(device);
1791 const auto voicespan = context->getVoicesSpan();
1792 std::for_each(voicespan.begin(), voicespan.end(), reset_voice);
1794 /* Clear all voice props to let them get allocated again. */
1795 context->mVoicePropClusters.clear();
1796 context->mFreeVoiceProps.store(nullptr, std::memory_order_relaxed);
1797 srclock.unlock();
1799 context->mPropsDirty = false;
1800 UpdateContextProps(context);
1801 UpdateAllEffectSlotProps(context);
1802 UpdateAllSourceProps(context);
1804 auto ctxspan = al::span{*device->mContexts.load()};
1805 std::for_each(ctxspan.begin(), ctxspan.end(), reset_context);
1806 mixer_mode.leave();
1808 device->mDeviceState = DeviceState::Configured;
1809 if(!device->Flags.test(DevicePaused))
1811 try {
1812 auto backend = device->Backend.get();
1813 backend->start();
1814 device->mDeviceState = DeviceState::Playing;
1816 catch(al::backend_exception& e) {
1817 ERR("%s\n", e.what());
1818 device->handleDisconnect("%s", e.what());
1819 return ALC_INVALID_DEVICE;
1821 TRACE("Post-start: %s, %s, %uhz, %u / %u buffer\n",
1822 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
1823 device->Frequency, device->UpdateSize, device->BufferSize);
1826 return ALC_NO_ERROR;
1830 * Updates device parameters as above, and also first clears the disconnected
1831 * status, if set.
1833 bool ResetDeviceParams(ALCdevice *device, const al::span<const int> attrList)
1835 /* If the device was disconnected, reset it since we're opened anew. */
1836 if(!device->Connected.load(std::memory_order_relaxed)) UNLIKELY
1838 /* Make sure disconnection is finished before continuing on. */
1839 std::ignore = device->waitForMix();
1841 for(ContextBase *ctxbase : *device->mContexts.load(std::memory_order_acquire))
1843 auto *ctx = static_cast<ALCcontext*>(ctxbase);
1844 if(!ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire))
1845 continue;
1847 /* Clear any pending voice changes and reallocate voices to get a
1848 * clean restart.
1850 std::lock_guard<std::mutex> sourcelock{ctx->mSourceLock};
1851 auto *vchg = ctx->mCurrentVoiceChange.load(std::memory_order_acquire);
1852 while(auto *next = vchg->mNext.load(std::memory_order_acquire))
1853 vchg = next;
1854 ctx->mCurrentVoiceChange.store(vchg, std::memory_order_release);
1856 ctx->mVoicePropClusters.clear();
1857 ctx->mFreeVoiceProps.store(nullptr, std::memory_order_relaxed);
1859 ctx->mVoiceClusters.clear();
1860 ctx->allocVoices(std::max<size_t>(256,
1861 ctx->mActiveVoiceCount.load(std::memory_order_relaxed)));
1864 device->Connected.store(true);
1867 ALCenum err{UpdateDeviceParams(device, attrList)};
1868 if(err == ALC_NO_ERROR) LIKELY return ALC_TRUE;
1870 alcSetError(device, err);
1871 return ALC_FALSE;
1875 /** Checks if the device handle is valid, and returns a new reference if so. */
1876 DeviceRef VerifyDevice(ALCdevice *device)
1878 std::lock_guard<std::recursive_mutex> listlock{ListLock};
1879 auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device);
1880 if(iter != DeviceList.end() && *iter == device)
1882 (*iter)->add_ref();
1883 return DeviceRef{*iter};
1885 return nullptr;
1890 * Checks if the given context is valid, returning a new reference to it if so.
1892 ContextRef VerifyContext(ALCcontext *context)
1894 std::lock_guard<std::recursive_mutex> listlock{ListLock};
1895 auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context);
1896 if(iter != ContextList.end() && *iter == context)
1898 (*iter)->add_ref();
1899 return ContextRef{*iter};
1901 return nullptr;
1904 } // namespace
1906 FORCE_ALIGN void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback, void *userptr) noexcept
1908 al_set_log_callback(callback, userptr);
1911 /** Returns a new reference to the currently active context for this thread. */
1912 ContextRef GetContextRef() noexcept
1914 ALCcontext *context{ALCcontext::getThreadContext()};
1915 if(context)
1916 context->add_ref();
1917 else
1919 while(ALCcontext::sGlobalContextLock.exchange(true, std::memory_order_acquire)) {
1920 /* Wait to make sure another thread isn't trying to change the
1921 * current context and bring its refcount to 0.
1924 context = ALCcontext::sGlobalContext.load(std::memory_order_acquire);
1925 if(context) LIKELY context->add_ref();
1926 ALCcontext::sGlobalContextLock.store(false, std::memory_order_release);
1928 return ContextRef{context};
1931 void alcSetError(ALCdevice *device, ALCenum errorCode)
1933 WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode);
1934 if(TrapALCError)
1936 #ifdef _WIN32
1937 /* DebugBreak() will cause an exception if there is no debugger */
1938 if(IsDebuggerPresent())
1939 DebugBreak();
1940 #elif defined(SIGTRAP)
1941 raise(SIGTRAP);
1942 #endif
1945 if(device)
1946 device->LastError.store(errorCode);
1947 else
1948 LastNullDeviceError.store(errorCode);
1951 /************************************************
1952 * Standard ALC functions
1953 ************************************************/
1955 ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) noexcept
1957 DeviceRef dev{VerifyDevice(device)};
1958 if(dev) return dev->LastError.exchange(ALC_NO_ERROR);
1959 return LastNullDeviceError.exchange(ALC_NO_ERROR);
1963 ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) noexcept
1965 ContextRef ctx{VerifyContext(context)};
1966 if(!ctx)
1968 alcSetError(nullptr, ALC_INVALID_CONTEXT);
1969 return;
1972 if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
1973 ctx->debugMessage(DebugSource::API, DebugType::Portability, 0, DebugSeverity::Medium,
1974 "alcSuspendContext behavior is not portable -- some implementations suspend all "
1975 "rendering, some only defer property changes, and some are completely no-op; consider "
1976 "using alcDevicePauseSOFT to suspend all rendering, or alDeferUpdatesSOFT to only "
1977 "defer property changes");
1979 if(SuspendDefers)
1981 std::lock_guard<std::mutex> proplock{ctx->mPropLock};
1982 ctx->deferUpdates();
1986 ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) noexcept
1988 ContextRef ctx{VerifyContext(context)};
1989 if(!ctx)
1991 alcSetError(nullptr, ALC_INVALID_CONTEXT);
1992 return;
1995 if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
1996 ctx->debugMessage(DebugSource::API, DebugType::Portability, 0, DebugSeverity::Medium,
1997 "alcProcessContext behavior is not portable -- some implementations resume rendering, "
1998 "some apply deferred property changes, and some are completely no-op; consider using "
1999 "alcDeviceResumeSOFT to resume rendering, or alProcessUpdatesSOFT to apply deferred "
2000 "property changes");
2002 if(SuspendDefers)
2004 std::lock_guard<std::mutex> proplock{ctx->mPropLock};
2005 ctx->processUpdates();
2010 ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param) noexcept
2012 const ALCchar *value{nullptr};
2014 switch(param)
2016 case ALC_NO_ERROR: value = GetNoErrorString(); break;
2017 case ALC_INVALID_ENUM: value = GetInvalidEnumString(); break;
2018 case ALC_INVALID_VALUE: value = GetInvalidValueString(); break;
2019 case ALC_INVALID_DEVICE: value = GetInvalidDeviceString(); break;
2020 case ALC_INVALID_CONTEXT: value = GetInvalidContextString(); break;
2021 case ALC_OUT_OF_MEMORY: value = GetOutOfMemoryString(); break;
2023 case ALC_DEVICE_SPECIFIER:
2024 value = GetDefaultName();
2025 break;
2027 case ALC_ALL_DEVICES_SPECIFIER:
2028 if(DeviceRef dev{VerifyDevice(Device)})
2030 if(dev->Type == DeviceType::Capture)
2031 alcSetError(dev.get(), ALC_INVALID_ENUM);
2032 else if(dev->Type == DeviceType::Loopback)
2033 value = GetDefaultName();
2034 else
2036 std::lock_guard<std::mutex> statelock{dev->StateLock};
2037 value = dev->DeviceName.c_str();
2040 else
2042 ProbeAllDevicesList();
2043 value = alcAllDevicesList.c_str();
2045 break;
2047 case ALC_CAPTURE_DEVICE_SPECIFIER:
2048 if(DeviceRef dev{VerifyDevice(Device)})
2050 if(dev->Type != DeviceType::Capture)
2051 alcSetError(dev.get(), ALC_INVALID_ENUM);
2052 else
2054 std::lock_guard<std::mutex> statelock{dev->StateLock};
2055 value = dev->DeviceName.c_str();
2058 else
2060 ProbeCaptureDeviceList();
2061 value = alcCaptureDeviceList.c_str();
2063 break;
2065 /* Default devices are always first in the list */
2066 case ALC_DEFAULT_DEVICE_SPECIFIER:
2067 value = GetDefaultName();
2068 break;
2070 case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
2071 if(alcAllDevicesList.empty())
2072 ProbeAllDevicesList();
2074 /* Copy first entry as default. */
2075 if(alcAllDevicesArray.empty())
2076 value = GetDefaultName();
2077 else
2079 alcDefaultAllDevicesSpecifier = alcAllDevicesArray.front();
2080 value = alcDefaultAllDevicesSpecifier.c_str();
2082 break;
2084 case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
2085 if(alcCaptureDeviceList.empty())
2086 ProbeCaptureDeviceList();
2088 /* Copy first entry as default. */
2089 if(alcCaptureDeviceArray.empty())
2090 value = GetDefaultName();
2091 else
2093 alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceArray.front();
2094 value = alcCaptureDefaultDeviceSpecifier.c_str();
2096 break;
2098 case ALC_EXTENSIONS:
2099 if(VerifyDevice(Device))
2100 value = GetExtensionList().data();
2101 else
2102 value = GetNoDeviceExtList().data();
2103 break;
2105 case ALC_HRTF_SPECIFIER_SOFT:
2106 if(DeviceRef dev{VerifyDevice(Device)})
2108 std::lock_guard<std::mutex> statelock{dev->StateLock};
2109 value = (dev->mHrtf ? dev->mHrtfName.c_str() : "");
2111 else
2112 alcSetError(nullptr, ALC_INVALID_DEVICE);
2113 break;
2115 default:
2116 alcSetError(VerifyDevice(Device).get(), ALC_INVALID_ENUM);
2117 break;
2120 return value;
2124 static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span<int> values)
2126 if(values.empty())
2128 alcSetError(device, ALC_INVALID_VALUE);
2129 return 0;
2132 if(!device)
2134 switch(param)
2136 case ALC_MAJOR_VERSION:
2137 values[0] = alcMajorVersion;
2138 return 1;
2139 case ALC_MINOR_VERSION:
2140 values[0] = alcMinorVersion;
2141 return 1;
2143 case ALC_EFX_MAJOR_VERSION:
2144 values[0] = alcEFXMajorVersion;
2145 return 1;
2146 case ALC_EFX_MINOR_VERSION:
2147 values[0] = alcEFXMinorVersion;
2148 return 1;
2149 case ALC_MAX_AUXILIARY_SENDS:
2150 values[0] = MaxSendCount;
2151 return 1;
2153 case ALC_ATTRIBUTES_SIZE:
2154 case ALC_ALL_ATTRIBUTES:
2155 case ALC_FREQUENCY:
2156 case ALC_REFRESH:
2157 case ALC_SYNC:
2158 case ALC_MONO_SOURCES:
2159 case ALC_STEREO_SOURCES:
2160 case ALC_CAPTURE_SAMPLES:
2161 case ALC_FORMAT_CHANNELS_SOFT:
2162 case ALC_FORMAT_TYPE_SOFT:
2163 case ALC_AMBISONIC_LAYOUT_SOFT:
2164 case ALC_AMBISONIC_SCALING_SOFT:
2165 case ALC_AMBISONIC_ORDER_SOFT:
2166 case ALC_MAX_AMBISONIC_ORDER_SOFT:
2167 alcSetError(nullptr, ALC_INVALID_DEVICE);
2168 return 0;
2170 default:
2171 alcSetError(nullptr, ALC_INVALID_ENUM);
2173 return 0;
2176 std::lock_guard<std::mutex> statelock{device->StateLock};
2177 if(device->Type == DeviceType::Capture)
2179 static constexpr int MaxCaptureAttributes{9};
2180 switch(param)
2182 case ALC_ATTRIBUTES_SIZE:
2183 values[0] = MaxCaptureAttributes;
2184 return 1;
2185 case ALC_ALL_ATTRIBUTES:
2186 if(values.size() >= MaxCaptureAttributes)
2188 size_t i{0};
2189 values[i++] = ALC_MAJOR_VERSION;
2190 values[i++] = alcMajorVersion;
2191 values[i++] = ALC_MINOR_VERSION;
2192 values[i++] = alcMinorVersion;
2193 values[i++] = ALC_CAPTURE_SAMPLES;
2194 values[i++] = static_cast<int>(device->Backend->availableSamples());
2195 values[i++] = ALC_CONNECTED;
2196 values[i++] = device->Connected.load(std::memory_order_relaxed);
2197 values[i++] = 0;
2198 assert(i == MaxCaptureAttributes);
2199 return i;
2201 alcSetError(device, ALC_INVALID_VALUE);
2202 return 0;
2204 case ALC_MAJOR_VERSION:
2205 values[0] = alcMajorVersion;
2206 return 1;
2207 case ALC_MINOR_VERSION:
2208 values[0] = alcMinorVersion;
2209 return 1;
2211 case ALC_CAPTURE_SAMPLES:
2212 values[0] = static_cast<int>(device->Backend->availableSamples());
2213 return 1;
2215 case ALC_CONNECTED:
2216 values[0] = device->Connected.load(std::memory_order_acquire);
2217 return 1;
2219 default:
2220 alcSetError(device, ALC_INVALID_ENUM);
2222 return 0;
2225 /* render device */
2226 auto NumAttrsForDevice = [](const ALCdevice *aldev) noexcept -> uint8_t
2228 if(aldev->Type == DeviceType::Loopback && aldev->FmtChans == DevFmtAmbi3D)
2229 return 37;
2230 return 31;
2232 switch(param)
2234 case ALC_ATTRIBUTES_SIZE:
2235 values[0] = NumAttrsForDevice(device);
2236 return 1;
2238 case ALC_ALL_ATTRIBUTES:
2239 if(values.size() >= NumAttrsForDevice(device))
2241 size_t i{0};
2242 values[i++] = ALC_MAJOR_VERSION;
2243 values[i++] = alcMajorVersion;
2244 values[i++] = ALC_MINOR_VERSION;
2245 values[i++] = alcMinorVersion;
2246 values[i++] = ALC_EFX_MAJOR_VERSION;
2247 values[i++] = alcEFXMajorVersion;
2248 values[i++] = ALC_EFX_MINOR_VERSION;
2249 values[i++] = alcEFXMinorVersion;
2251 values[i++] = ALC_FREQUENCY;
2252 values[i++] = static_cast<int>(device->Frequency);
2253 if(device->Type != DeviceType::Loopback)
2255 values[i++] = ALC_REFRESH;
2256 values[i++] = static_cast<int>(device->Frequency / device->UpdateSize);
2258 values[i++] = ALC_SYNC;
2259 values[i++] = ALC_FALSE;
2261 else
2263 if(device->FmtChans == DevFmtAmbi3D)
2265 values[i++] = ALC_AMBISONIC_LAYOUT_SOFT;
2266 values[i++] = EnumFromDevAmbi(device->mAmbiLayout);
2268 values[i++] = ALC_AMBISONIC_SCALING_SOFT;
2269 values[i++] = EnumFromDevAmbi(device->mAmbiScale);
2271 values[i++] = ALC_AMBISONIC_ORDER_SOFT;
2272 values[i++] = static_cast<int>(device->mAmbiOrder);
2275 values[i++] = ALC_FORMAT_CHANNELS_SOFT;
2276 values[i++] = EnumFromDevFmt(device->FmtChans);
2278 values[i++] = ALC_FORMAT_TYPE_SOFT;
2279 values[i++] = EnumFromDevFmt(device->FmtType);
2282 values[i++] = ALC_MONO_SOURCES;
2283 values[i++] = static_cast<int>(device->NumMonoSources);
2285 values[i++] = ALC_STEREO_SOURCES;
2286 values[i++] = static_cast<int>(device->NumStereoSources);
2288 values[i++] = ALC_MAX_AUXILIARY_SENDS;
2289 values[i++] = static_cast<int>(device->NumAuxSends);
2291 values[i++] = ALC_HRTF_SOFT;
2292 values[i++] = (device->mHrtf ? ALC_TRUE : ALC_FALSE);
2294 values[i++] = ALC_HRTF_STATUS_SOFT;
2295 values[i++] = device->mHrtfStatus;
2297 values[i++] = ALC_OUTPUT_LIMITER_SOFT;
2298 values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE;
2300 values[i++] = ALC_MAX_AMBISONIC_ORDER_SOFT;
2301 values[i++] = MaxAmbiOrder;
2303 values[i++] = ALC_OUTPUT_MODE_SOFT;
2304 values[i++] = static_cast<ALCenum>(device->getOutputMode1());
2306 values[i++] = 0;
2307 assert(i == NumAttrsForDevice(device));
2308 return i;
2310 alcSetError(device, ALC_INVALID_VALUE);
2311 return 0;
2313 case ALC_MAJOR_VERSION:
2314 values[0] = alcMajorVersion;
2315 return 1;
2317 case ALC_MINOR_VERSION:
2318 values[0] = alcMinorVersion;
2319 return 1;
2321 case ALC_EFX_MAJOR_VERSION:
2322 values[0] = alcEFXMajorVersion;
2323 return 1;
2325 case ALC_EFX_MINOR_VERSION:
2326 values[0] = alcEFXMinorVersion;
2327 return 1;
2329 case ALC_FREQUENCY:
2330 values[0] = static_cast<int>(device->Frequency);
2331 return 1;
2333 case ALC_REFRESH:
2334 if(device->Type == DeviceType::Loopback)
2336 alcSetError(device, ALC_INVALID_DEVICE);
2337 return 0;
2339 values[0] = static_cast<int>(device->Frequency / device->UpdateSize);
2340 return 1;
2342 case ALC_SYNC:
2343 if(device->Type == DeviceType::Loopback)
2345 alcSetError(device, ALC_INVALID_DEVICE);
2346 return 0;
2348 values[0] = ALC_FALSE;
2349 return 1;
2351 case ALC_FORMAT_CHANNELS_SOFT:
2352 if(device->Type != DeviceType::Loopback)
2354 alcSetError(device, ALC_INVALID_DEVICE);
2355 return 0;
2357 values[0] = EnumFromDevFmt(device->FmtChans);
2358 return 1;
2360 case ALC_FORMAT_TYPE_SOFT:
2361 if(device->Type != DeviceType::Loopback)
2363 alcSetError(device, ALC_INVALID_DEVICE);
2364 return 0;
2366 values[0] = EnumFromDevFmt(device->FmtType);
2367 return 1;
2369 case ALC_AMBISONIC_LAYOUT_SOFT:
2370 if(device->Type != DeviceType::Loopback || device->FmtChans != DevFmtAmbi3D)
2372 alcSetError(device, ALC_INVALID_DEVICE);
2373 return 0;
2375 values[0] = EnumFromDevAmbi(device->mAmbiLayout);
2376 return 1;
2378 case ALC_AMBISONIC_SCALING_SOFT:
2379 if(device->Type != DeviceType::Loopback || device->FmtChans != DevFmtAmbi3D)
2381 alcSetError(device, ALC_INVALID_DEVICE);
2382 return 0;
2384 values[0] = EnumFromDevAmbi(device->mAmbiScale);
2385 return 1;
2387 case ALC_AMBISONIC_ORDER_SOFT:
2388 if(device->Type != DeviceType::Loopback || device->FmtChans != DevFmtAmbi3D)
2390 alcSetError(device, ALC_INVALID_DEVICE);
2391 return 0;
2393 values[0] = static_cast<int>(device->mAmbiOrder);
2394 return 1;
2396 case ALC_MONO_SOURCES:
2397 values[0] = static_cast<int>(device->NumMonoSources);
2398 return 1;
2400 case ALC_STEREO_SOURCES:
2401 values[0] = static_cast<int>(device->NumStereoSources);
2402 return 1;
2404 case ALC_MAX_AUXILIARY_SENDS:
2405 values[0] = static_cast<int>(device->NumAuxSends);
2406 return 1;
2408 case ALC_CONNECTED:
2409 values[0] = device->Connected.load(std::memory_order_acquire);
2410 return 1;
2412 case ALC_HRTF_SOFT:
2413 values[0] = (device->mHrtf ? ALC_TRUE : ALC_FALSE);
2414 return 1;
2416 case ALC_HRTF_STATUS_SOFT:
2417 values[0] = device->mHrtfStatus;
2418 return 1;
2420 case ALC_NUM_HRTF_SPECIFIERS_SOFT:
2421 device->enumerateHrtfs();
2422 values[0] = static_cast<int>(std::min(device->mHrtfList.size(),
2423 size_t{std::numeric_limits<int>::max()}));
2424 return 1;
2426 case ALC_OUTPUT_LIMITER_SOFT:
2427 values[0] = device->Limiter ? ALC_TRUE : ALC_FALSE;
2428 return 1;
2430 case ALC_MAX_AMBISONIC_ORDER_SOFT:
2431 values[0] = MaxAmbiOrder;
2432 return 1;
2434 case ALC_OUTPUT_MODE_SOFT:
2435 values[0] = static_cast<ALCenum>(device->getOutputMode1());
2436 return 1;
2438 default:
2439 alcSetError(device, ALC_INVALID_ENUM);
2441 return 0;
2444 ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) noexcept
2446 DeviceRef dev{VerifyDevice(device)};
2447 if(size <= 0 || values == nullptr)
2448 alcSetError(dev.get(), ALC_INVALID_VALUE);
2449 else
2450 GetIntegerv(dev.get(), param, {values, static_cast<uint>(size)});
2453 ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values) noexcept
2455 DeviceRef dev{VerifyDevice(device)};
2456 if(size <= 0 || values == nullptr)
2458 alcSetError(dev.get(), ALC_INVALID_VALUE);
2459 return;
2461 const auto valuespan = al::span{values, static_cast<uint>(size)};
2462 if(!dev || dev->Type == DeviceType::Capture)
2464 auto ivals = std::vector<int>(valuespan.size());
2465 if(size_t got{GetIntegerv(dev.get(), pname, ivals)})
2466 std::copy_n(ivals.cbegin(), got, valuespan.begin());
2467 return;
2469 /* render device */
2470 auto NumAttrsForDevice = [](ALCdevice *aldev) noexcept -> size_t
2472 if(aldev->Type == DeviceType::Loopback && aldev->FmtChans == DevFmtAmbi3D)
2473 return 41;
2474 return 35;
2476 std::lock_guard<std::mutex> statelock{dev->StateLock};
2477 switch(pname)
2479 case ALC_ATTRIBUTES_SIZE:
2480 valuespan[0] = static_cast<ALCint64SOFT>(NumAttrsForDevice(dev.get()));
2481 break;
2483 case ALC_ALL_ATTRIBUTES:
2484 if(valuespan.size() < NumAttrsForDevice(dev.get()))
2485 alcSetError(dev.get(), ALC_INVALID_VALUE);
2486 else
2488 size_t i{0};
2489 valuespan[i++] = ALC_FREQUENCY;
2490 valuespan[i++] = dev->Frequency;
2492 if(dev->Type != DeviceType::Loopback)
2494 valuespan[i++] = ALC_REFRESH;
2495 valuespan[i++] = dev->Frequency / dev->UpdateSize;
2497 valuespan[i++] = ALC_SYNC;
2498 valuespan[i++] = ALC_FALSE;
2500 else
2502 valuespan[i++] = ALC_FORMAT_CHANNELS_SOFT;
2503 valuespan[i++] = EnumFromDevFmt(dev->FmtChans);
2505 valuespan[i++] = ALC_FORMAT_TYPE_SOFT;
2506 valuespan[i++] = EnumFromDevFmt(dev->FmtType);
2508 if(dev->FmtChans == DevFmtAmbi3D)
2510 valuespan[i++] = ALC_AMBISONIC_LAYOUT_SOFT;
2511 valuespan[i++] = EnumFromDevAmbi(dev->mAmbiLayout);
2513 valuespan[i++] = ALC_AMBISONIC_SCALING_SOFT;
2514 valuespan[i++] = EnumFromDevAmbi(dev->mAmbiScale);
2516 valuespan[i++] = ALC_AMBISONIC_ORDER_SOFT;
2517 valuespan[i++] = dev->mAmbiOrder;
2521 valuespan[i++] = ALC_MONO_SOURCES;
2522 valuespan[i++] = dev->NumMonoSources;
2524 valuespan[i++] = ALC_STEREO_SOURCES;
2525 valuespan[i++] = dev->NumStereoSources;
2527 valuespan[i++] = ALC_MAX_AUXILIARY_SENDS;
2528 valuespan[i++] = dev->NumAuxSends;
2530 valuespan[i++] = ALC_HRTF_SOFT;
2531 valuespan[i++] = (dev->mHrtf ? ALC_TRUE : ALC_FALSE);
2533 valuespan[i++] = ALC_HRTF_STATUS_SOFT;
2534 valuespan[i++] = dev->mHrtfStatus;
2536 valuespan[i++] = ALC_OUTPUT_LIMITER_SOFT;
2537 valuespan[i++] = dev->Limiter ? ALC_TRUE : ALC_FALSE;
2539 ClockLatency clock{GetClockLatency(dev.get(), dev->Backend.get())};
2540 valuespan[i++] = ALC_DEVICE_CLOCK_SOFT;
2541 valuespan[i++] = clock.ClockTime.count();
2543 valuespan[i++] = ALC_DEVICE_LATENCY_SOFT;
2544 valuespan[i++] = clock.Latency.count();
2546 valuespan[i++] = ALC_OUTPUT_MODE_SOFT;
2547 valuespan[i++] = al::to_underlying(device->getOutputMode1());
2549 valuespan[i++] = 0;
2551 break;
2553 case ALC_DEVICE_CLOCK_SOFT:
2555 uint samplecount, refcount;
2556 nanoseconds basecount;
2557 do {
2558 refcount = dev->waitForMix();
2559 basecount = dev->mClockBase.load(std::memory_order_relaxed);
2560 samplecount = dev->mSamplesDone.load(std::memory_order_relaxed);
2561 std::atomic_thread_fence(std::memory_order_acquire);
2562 } while(refcount != dev->mMixCount.load(std::memory_order_relaxed));
2563 basecount += nanoseconds{seconds{samplecount}} / dev->Frequency;
2564 valuespan[0] = basecount.count();
2566 break;
2568 case ALC_DEVICE_LATENCY_SOFT:
2569 valuespan[0] = GetClockLatency(dev.get(), dev->Backend.get()).Latency.count();
2570 break;
2572 case ALC_DEVICE_CLOCK_LATENCY_SOFT:
2573 if(size < 2)
2574 alcSetError(dev.get(), ALC_INVALID_VALUE);
2575 else
2577 ClockLatency clock{GetClockLatency(dev.get(), dev->Backend.get())};
2578 valuespan[0] = clock.ClockTime.count();
2579 valuespan[1] = clock.Latency.count();
2581 break;
2583 default:
2584 auto ivals = std::vector<int>(valuespan.size());
2585 if(size_t got{GetIntegerv(dev.get(), pname, ivals)})
2586 std::copy_n(ivals.cbegin(), got, valuespan.begin());
2587 break;
2592 ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName) noexcept
2594 DeviceRef dev{VerifyDevice(device)};
2595 if(!extName)
2597 alcSetError(dev.get(), ALC_INVALID_VALUE);
2598 return ALC_FALSE;
2601 const std::string_view tofind{extName};
2602 const auto extlist = dev ? GetExtensionList() : GetNoDeviceExtList();
2603 auto matchpos = extlist.find(tofind);
2604 while(matchpos != std::string_view::npos)
2606 const auto endpos = matchpos + tofind.size();
2607 if((matchpos == 0 || std::isspace(extlist[matchpos-1]))
2608 && (endpos == extlist.size() || std::isspace(extlist[endpos])))
2609 return ALC_TRUE;
2610 matchpos = extlist.find(tofind, matchpos+1);
2612 return ALC_FALSE;
2616 ALCvoid* ALC_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALCchar *funcName) noexcept
2617 { return alcGetProcAddress(device, funcName); }
2619 ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName) noexcept
2621 if(!funcName)
2623 DeviceRef dev{VerifyDevice(device)};
2624 alcSetError(dev.get(), ALC_INVALID_VALUE);
2625 return nullptr;
2628 #ifdef ALSOFT_EAX
2629 if(eax_g_is_enabled)
2631 for(const auto &func : eaxFunctions)
2633 if(strcmp(func.funcName, funcName) == 0)
2634 return func.address;
2637 #endif
2638 for(const auto &func : alcFunctions)
2640 if(strcmp(func.funcName, funcName) == 0)
2641 return func.address;
2643 return nullptr;
2647 ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName) noexcept
2649 if(!enumName)
2651 DeviceRef dev{VerifyDevice(device)};
2652 alcSetError(dev.get(), ALC_INVALID_VALUE);
2653 return 0;
2656 #ifdef ALSOFT_EAX
2657 if(eax_g_is_enabled)
2659 for(const auto &enm : eaxEnumerations)
2661 if(strcmp(enm.enumName, enumName) == 0)
2662 return enm.value;
2665 #endif
2666 for(const auto &enm : alcEnumerations)
2668 if(strcmp(enm.enumName, enumName) == 0)
2669 return enm.value;
2672 return 0;
2676 ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList) noexcept
2678 /* Explicitly hold the list lock while taking the StateLock in case the
2679 * device is asynchronously destroyed, to ensure this new context is
2680 * properly cleaned up after being made.
2682 std::unique_lock<std::recursive_mutex> listlock{ListLock};
2683 DeviceRef dev{VerifyDevice(device)};
2684 if(!dev || dev->Type == DeviceType::Capture || !dev->Connected.load(std::memory_order_relaxed))
2686 listlock.unlock();
2687 alcSetError(dev.get(), ALC_INVALID_DEVICE);
2688 return nullptr;
2690 std::unique_lock<std::mutex> statelock{dev->StateLock};
2691 listlock.unlock();
2693 dev->LastError.store(ALC_NO_ERROR);
2695 const auto attrSpan = SpanFromAttributeList(attrList);
2696 ALCenum err{UpdateDeviceParams(dev.get(), attrSpan)};
2697 if(err != ALC_NO_ERROR)
2699 alcSetError(dev.get(), err);
2700 return nullptr;
2703 ContextFlagBitset ctxflags{0};
2704 for(size_t i{0};i < attrSpan.size();i+=2)
2706 if(attrSpan[i] == ALC_CONTEXT_FLAGS_EXT)
2708 ctxflags = static_cast<ALuint>(attrSpan[i+1]);
2709 break;
2713 auto context = ContextRef{new(std::nothrow) ALCcontext{dev, ctxflags}};
2714 if(!context)
2716 alcSetError(dev.get(), ALC_OUT_OF_MEMORY);
2717 return nullptr;
2719 context->init();
2721 if(auto volopt = dev->configValue<float>({}, "volume-adjust"))
2723 const float valf{*volopt};
2724 if(!std::isfinite(valf))
2725 ERR("volume-adjust must be finite: %f\n", valf);
2726 else
2728 const float db{std::clamp(valf, -24.0f, 24.0f)};
2729 if(db != valf)
2730 WARN("volume-adjust clamped: %f, range: +/-%f\n", valf, 24.0f);
2731 context->mGainBoost = std::pow(10.0f, db/20.0f);
2732 TRACE("volume-adjust gain: %f\n", context->mGainBoost);
2737 using ContextArray = al::FlexArray<ContextBase*>;
2739 /* Allocate a new context array, which holds 1 more than the current/
2740 * old array.
2742 auto *oldarray = device->mContexts.load();
2743 auto newarray = ContextArray::Create(oldarray->size() + 1);
2745 /* Copy the current/old context handles to the new array, appending the
2746 * new context.
2748 auto iter = std::copy(oldarray->begin(), oldarray->end(), newarray->begin());
2749 *iter = context.get();
2751 /* Store the new context array in the device. Wait for any current mix
2752 * to finish before deleting the old array.
2754 auto prevarray = dev->mContexts.exchange(std::move(newarray));
2755 std::ignore = dev->waitForMix();
2757 statelock.unlock();
2760 listlock.lock();
2761 auto iter = std::lower_bound(ContextList.cbegin(), ContextList.cend(), context.get());
2762 ContextList.emplace(iter, context.get());
2763 listlock.unlock();
2766 if(ALeffectslot *slot{context->mDefaultSlot.get()})
2768 ALenum sloterr{slot->initEffect(0, ALCcontext::sDefaultEffect.type,
2769 ALCcontext::sDefaultEffect.Props, context.get())};
2770 if(sloterr == AL_NO_ERROR)
2771 slot->updateProps(context.get());
2772 else
2773 ERR("Failed to initialize the default effect\n");
2776 TRACE("Created context %p\n", voidp{context.get()});
2777 return context.release();
2780 ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept
2782 std::unique_lock<std::recursive_mutex> listlock{ListLock};
2783 auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context);
2784 if(iter == ContextList.end() || *iter != context)
2786 listlock.unlock();
2787 alcSetError(nullptr, ALC_INVALID_CONTEXT);
2788 return;
2791 /* Hold a reference to this context so it remains valid until the ListLock
2792 * is released.
2794 ContextRef ctx{*iter};
2795 ContextList.erase(iter);
2797 ALCdevice *Device{ctx->mALDevice.get()};
2799 std::lock_guard<std::mutex> statelock{Device->StateLock};
2800 ctx->deinit();
2804 ALC_API auto ALC_APIENTRY alcGetCurrentContext() noexcept -> ALCcontext*
2806 ALCcontext *Context{ALCcontext::getThreadContext()};
2807 if(!Context) Context = ALCcontext::sGlobalContext.load();
2808 return Context;
2811 /** Returns the currently active thread-local context. */
2812 ALC_API auto ALC_APIENTRY alcGetThreadContext() noexcept -> ALCcontext*
2813 { return ALCcontext::getThreadContext(); }
2815 ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexcept
2817 /* context must be valid or nullptr */
2818 ContextRef ctx;
2819 if(context)
2821 ctx = VerifyContext(context);
2822 if(!ctx)
2824 alcSetError(nullptr, ALC_INVALID_CONTEXT);
2825 return ALC_FALSE;
2828 /* Release this reference (if any) to store it in the GlobalContext
2829 * pointer. Take ownership of the reference (if any) that was previously
2830 * stored there, and let the reference go.
2832 while(ALCcontext::sGlobalContextLock.exchange(true, std::memory_order_acquire)) {
2833 /* Wait to make sure another thread isn't getting or trying to change
2834 * the current context as its refcount is decremented.
2837 ctx = ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())};
2838 ALCcontext::sGlobalContextLock.store(false, std::memory_order_release);
2840 /* Take ownership of the thread-local context reference (if any), clearing
2841 * the storage to null.
2843 ctx = ContextRef{ALCcontext::getThreadContext()};
2844 if(ctx) ALCcontext::setThreadContext(nullptr);
2845 /* Reset (decrement) the previous thread-local reference. */
2847 return ALC_TRUE;
2850 /** Makes the given context the active context for the current thread. */
2851 ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) noexcept
2853 /* context must be valid or nullptr */
2854 ContextRef ctx;
2855 if(context)
2857 ctx = VerifyContext(context);
2858 if(!ctx)
2860 alcSetError(nullptr, ALC_INVALID_CONTEXT);
2861 return ALC_FALSE;
2864 /* context's reference count is already incremented */
2865 ContextRef old{ALCcontext::getThreadContext()};
2866 ALCcontext::setThreadContext(ctx.release());
2868 return ALC_TRUE;
2872 ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context) noexcept
2874 ContextRef ctx{VerifyContext(Context)};
2875 if(!ctx)
2877 alcSetError(nullptr, ALC_INVALID_CONTEXT);
2878 return nullptr;
2880 return ctx->mALDevice.get();
2884 ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) noexcept
2886 InitConfig();
2888 if(!PlaybackFactory)
2890 alcSetError(nullptr, ALC_INVALID_VALUE);
2891 return nullptr;
2894 std::string_view devname{deviceName ? deviceName : ""};
2895 if(!devname.empty())
2897 TRACE("Opening playback device \"%.*s\"\n", al::sizei(devname), devname.data());
2898 if(al::case_compare(devname, GetDefaultName()) == 0
2899 #ifdef _WIN32
2900 /* Some old Windows apps hardcode these expecting OpenAL to use a
2901 * specific audio API, even when they're not enumerated. Creative's
2902 * router effectively ignores them too.
2904 || al::case_compare(devname, "DirectSound3D"sv) == 0
2905 || al::case_compare(devname, "DirectSound"sv) == 0
2906 || al::case_compare(devname, "MMSYSTEM"sv) == 0
2907 #endif
2908 /* Some old Linux apps hardcode configuration strings that were
2909 * supported by the OpenAL SI. We can't really do anything useful
2910 * with them, so just ignore.
2912 || al::starts_with(devname, "'("sv)
2913 || al::case_compare(devname, "openal-soft"sv) == 0)
2914 devname = {};
2916 else
2917 TRACE("Opening default playback device\n");
2919 const uint DefaultSends{
2920 #ifdef ALSOFT_EAX
2921 eax_g_is_enabled ? uint{EAX_MAX_FXSLOTS} :
2922 #endif // ALSOFT_EAX
2923 uint{DefaultSendCount}
2926 DeviceRef device{new(std::nothrow) ALCdevice{DeviceType::Playback}};
2927 if(!device)
2929 WARN("Failed to create playback device handle\n");
2930 alcSetError(nullptr, ALC_OUT_OF_MEMORY);
2931 return nullptr;
2934 /* Set output format */
2935 device->FmtChans = DevFmtChannelsDefault;
2936 device->FmtType = DevFmtTypeDefault;
2937 device->Frequency = DefaultOutputRate;
2938 device->UpdateSize = DefaultUpdateSize;
2939 device->BufferSize = DefaultUpdateSize * DefaultNumUpdates;
2941 device->SourcesMax = 256;
2942 device->NumStereoSources = 1;
2943 device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
2944 device->AuxiliaryEffectSlotMax = 64;
2945 device->NumAuxSends = DefaultSends;
2947 try {
2948 auto backend = PlaybackFactory->createBackend(device.get(), BackendType::Playback);
2949 std::lock_guard<std::recursive_mutex> listlock{ListLock};
2950 backend->open(devname);
2951 device->Backend = std::move(backend);
2953 catch(al::backend_exception &e) {
2954 WARN("Failed to open playback device: %s\n", e.what());
2955 alcSetError(nullptr, (e.errorCode() == al::backend_error::OutOfMemory)
2956 ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE);
2957 return nullptr;
2961 std::lock_guard<std::recursive_mutex> listlock{ListLock};
2962 auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get());
2963 DeviceList.emplace(iter, device.get());
2966 TRACE("Created device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str());
2967 return device.release();
2970 ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept
2972 std::unique_lock<std::recursive_mutex> listlock{ListLock};
2973 auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device);
2974 if(iter == DeviceList.end() || *iter != device)
2976 alcSetError(nullptr, ALC_INVALID_DEVICE);
2977 return ALC_FALSE;
2979 if((*iter)->Type == DeviceType::Capture)
2981 alcSetError(*iter, ALC_INVALID_DEVICE);
2982 return ALC_FALSE;
2985 /* Erase the device, and any remaining contexts left on it, from their
2986 * respective lists.
2988 DeviceRef dev{*iter};
2989 DeviceList.erase(iter);
2991 std::unique_lock<std::mutex> statelock{dev->StateLock};
2992 std::vector<ContextRef> orphanctxs;
2993 for(ContextBase *ctx : *dev->mContexts.load())
2995 auto ctxiter = std::lower_bound(ContextList.begin(), ContextList.end(), ctx);
2996 if(ctxiter != ContextList.end() && *ctxiter == ctx)
2998 orphanctxs.emplace_back(*ctxiter);
2999 ContextList.erase(ctxiter);
3002 listlock.unlock();
3004 for(ContextRef &context : orphanctxs)
3006 WARN("Releasing orphaned context %p\n", voidp{context.get()});
3007 context->deinit();
3009 orphanctxs.clear();
3011 if(dev->mDeviceState == DeviceState::Playing)
3013 dev->Backend->stop();
3014 dev->mDeviceState = DeviceState::Configured;
3017 return ALC_TRUE;
3021 /************************************************
3022 * ALC capture functions
3023 ************************************************/
3024 ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples) noexcept
3026 InitConfig();
3028 if(!CaptureFactory)
3030 alcSetError(nullptr, ALC_INVALID_VALUE);
3031 return nullptr;
3034 if(samples <= 0)
3036 alcSetError(nullptr, ALC_INVALID_VALUE);
3037 return nullptr;
3040 std::string_view devname{deviceName ? deviceName : ""};
3041 if(!devname.empty())
3043 TRACE("Opening capture device \"%.*s\"\n", al::sizei(devname), devname.data());
3044 if(al::case_compare(devname, GetDefaultName()) == 0
3045 || al::case_compare(devname, "openal-soft"sv) == 0)
3046 devname = {};
3048 else
3049 TRACE("Opening default capture device\n");
3051 DeviceRef device{new(std::nothrow) ALCdevice{DeviceType::Capture}};
3052 if(!device)
3054 WARN("Failed to create capture device handle\n");
3055 alcSetError(nullptr, ALC_OUT_OF_MEMORY);
3056 return nullptr;
3059 auto decompfmt = DecomposeDevFormat(format);
3060 if(!decompfmt)
3062 alcSetError(nullptr, ALC_INVALID_ENUM);
3063 return nullptr;
3066 device->Frequency = frequency;
3067 device->FmtChans = decompfmt->chans;
3068 device->FmtType = decompfmt->type;
3069 device->Flags.set(FrequencyRequest);
3070 device->Flags.set(ChannelsRequest);
3071 device->Flags.set(SampleTypeRequest);
3073 device->UpdateSize = static_cast<uint>(samples);
3074 device->BufferSize = static_cast<uint>(samples);
3076 TRACE("Capture format: %s, %s, %uhz, %u / %u buffer\n", DevFmtChannelsString(device->FmtChans),
3077 DevFmtTypeString(device->FmtType), device->Frequency, device->UpdateSize,
3078 device->BufferSize);
3080 try {
3081 auto backend = CaptureFactory->createBackend(device.get(), BackendType::Capture);
3082 std::lock_guard<std::recursive_mutex> listlock{ListLock};
3083 backend->open(devname);
3084 device->Backend = std::move(backend);
3086 catch(al::backend_exception &e) {
3087 WARN("Failed to open capture device: %s\n", e.what());
3088 alcSetError(nullptr, (e.errorCode() == al::backend_error::OutOfMemory)
3089 ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE);
3090 return nullptr;
3094 std::lock_guard<std::recursive_mutex> listlock{ListLock};
3095 auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get());
3096 DeviceList.emplace(iter, device.get());
3098 device->mDeviceState = DeviceState::Configured;
3100 TRACE("Created capture device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str());
3101 return device.release();
3104 ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcept
3106 std::unique_lock<std::recursive_mutex> listlock{ListLock};
3107 auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device);
3108 if(iter == DeviceList.end() || *iter != device)
3110 alcSetError(nullptr, ALC_INVALID_DEVICE);
3111 return ALC_FALSE;
3113 if((*iter)->Type != DeviceType::Capture)
3115 alcSetError(*iter, ALC_INVALID_DEVICE);
3116 return ALC_FALSE;
3119 DeviceRef dev{*iter};
3120 DeviceList.erase(iter);
3121 listlock.unlock();
3123 std::lock_guard<std::mutex> statelock{dev->StateLock};
3124 if(dev->mDeviceState == DeviceState::Playing)
3126 dev->Backend->stop();
3127 dev->mDeviceState = DeviceState::Configured;
3130 return ALC_TRUE;
3133 ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) noexcept
3135 DeviceRef dev{VerifyDevice(device)};
3136 if(!dev || dev->Type != DeviceType::Capture)
3138 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3139 return;
3142 std::lock_guard<std::mutex> statelock{dev->StateLock};
3143 if(!dev->Connected.load(std::memory_order_acquire)
3144 || dev->mDeviceState < DeviceState::Configured)
3145 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3146 else if(dev->mDeviceState != DeviceState::Playing)
3148 try {
3149 auto backend = dev->Backend.get();
3150 backend->start();
3151 dev->mDeviceState = DeviceState::Playing;
3153 catch(al::backend_exception& e) {
3154 ERR("%s\n", e.what());
3155 dev->handleDisconnect("%s", e.what());
3156 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3161 ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) noexcept
3163 DeviceRef dev{VerifyDevice(device)};
3164 if(!dev || dev->Type != DeviceType::Capture)
3165 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3166 else
3168 std::lock_guard<std::mutex> statelock{dev->StateLock};
3169 if(dev->mDeviceState == DeviceState::Playing)
3171 dev->Backend->stop();
3172 dev->mDeviceState = DeviceState::Configured;
3177 ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept
3179 DeviceRef dev{VerifyDevice(device)};
3180 if(!dev || dev->Type != DeviceType::Capture)
3182 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3183 return;
3186 if(samples < 0 || (samples > 0 && buffer == nullptr))
3188 alcSetError(dev.get(), ALC_INVALID_VALUE);
3189 return;
3191 if(samples < 1)
3192 return;
3194 std::lock_guard<std::mutex> statelock{dev->StateLock};
3195 BackendBase *backend{dev->Backend.get()};
3197 const auto usamples = static_cast<uint>(samples);
3198 if(usamples > backend->availableSamples())
3200 alcSetError(dev.get(), ALC_INVALID_VALUE);
3201 return;
3204 backend->captureSamples(static_cast<std::byte*>(buffer), usamples);
3208 /************************************************
3209 * ALC loopback functions
3210 ************************************************/
3212 /** Open a loopback device, for manual rendering. */
3213 ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName) noexcept
3215 InitConfig();
3217 /* Make sure the device name, if specified, is us. */
3218 if(deviceName && strcmp(deviceName, GetDefaultName()) != 0)
3220 alcSetError(nullptr, ALC_INVALID_VALUE);
3221 return nullptr;
3224 const uint DefaultSends{
3225 #ifdef ALSOFT_EAX
3226 eax_g_is_enabled ? uint{EAX_MAX_FXSLOTS} :
3227 #endif // ALSOFT_EAX
3228 uint{DefaultSendCount}
3231 DeviceRef device{new(std::nothrow) ALCdevice{DeviceType::Loopback}};
3232 if(!device)
3234 WARN("Failed to create loopback device handle\n");
3235 alcSetError(nullptr, ALC_OUT_OF_MEMORY);
3236 return nullptr;
3239 device->SourcesMax = 256;
3240 device->AuxiliaryEffectSlotMax = 64;
3241 device->NumAuxSends = DefaultSends;
3243 //Set output format
3244 device->BufferSize = 0;
3245 device->UpdateSize = 0;
3247 device->Frequency = DefaultOutputRate;
3248 device->FmtChans = DevFmtChannelsDefault;
3249 device->FmtType = DevFmtTypeDefault;
3251 device->NumStereoSources = 1;
3252 device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
3254 try {
3255 auto backend = LoopbackBackendFactory::getFactory().createBackend(device.get(),
3256 BackendType::Playback);
3257 backend->open("Loopback");
3258 device->Backend = std::move(backend);
3260 catch(al::backend_exception &e) {
3261 WARN("Failed to open loopback device: %s\n", e.what());
3262 alcSetError(nullptr, (e.errorCode() == al::backend_error::OutOfMemory)
3263 ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE);
3264 return nullptr;
3268 std::lock_guard<std::recursive_mutex> listlock{ListLock};
3269 auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get());
3270 DeviceList.emplace(iter, device.get());
3273 TRACE("Created loopback device %p\n", voidp{device.get()});
3274 return device.release();
3278 * Determines if the loopback device supports the given format for rendering.
3280 ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) noexcept
3282 DeviceRef dev{VerifyDevice(device)};
3283 if(!dev || dev->Type != DeviceType::Loopback)
3284 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3285 else if(freq <= 0)
3286 alcSetError(dev.get(), ALC_INVALID_VALUE);
3287 else
3289 if(DevFmtTypeFromEnum(type).has_value() && DevFmtChannelsFromEnum(channels).has_value()
3290 && freq >= int{MinOutputRate} && freq <= int{MaxOutputRate})
3291 return ALC_TRUE;
3294 return ALC_FALSE;
3298 * Renders some samples into a buffer, using the format last set by the
3299 * attributes given to alcCreateContext.
3301 #if defined(__GNUC__) && defined(__i386__)
3302 /* Needed on x86-32 even without SSE codegen, since the mixer may still use SSE
3303 * and GCC assumes the stack is aligned (x86-64 ABI guarantees alignment).
3305 [[gnu::force_align_arg_pointer]]
3306 #endif
3307 ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept
3309 if(!device || device->Type != DeviceType::Loopback) UNLIKELY
3310 alcSetError(device, ALC_INVALID_DEVICE);
3311 else if(samples < 0 || (samples > 0 && buffer == nullptr)) UNLIKELY
3312 alcSetError(device, ALC_INVALID_VALUE);
3313 else
3314 device->renderSamples(buffer, static_cast<uint>(samples), device->channelsFromFmt());
3318 /************************************************
3319 * ALC DSP pause/resume functions
3320 ************************************************/
3322 /** Pause the DSP to stop audio processing. */
3323 ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) noexcept
3325 DeviceRef dev{VerifyDevice(device)};
3326 if(!dev || dev->Type != DeviceType::Playback)
3327 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3328 else
3330 std::lock_guard<std::mutex> statelock{dev->StateLock};
3331 if(dev->mDeviceState == DeviceState::Playing)
3333 dev->Backend->stop();
3334 dev->mDeviceState = DeviceState::Configured;
3336 dev->Flags.set(DevicePaused);
3340 /** Resume the DSP to restart audio processing. */
3341 ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept
3343 DeviceRef dev{VerifyDevice(device)};
3344 if(!dev || dev->Type != DeviceType::Playback)
3346 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3347 return;
3350 std::lock_guard<std::mutex> statelock{dev->StateLock};
3351 if(!dev->Flags.test(DevicePaused))
3352 return;
3353 if(dev->mDeviceState < DeviceState::Configured)
3355 WARN("Cannot resume unconfigured device\n");
3356 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3357 return;
3359 if(!dev->Connected.load())
3361 WARN("Cannot resume a disconnected device\n");
3362 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3363 return;
3365 dev->Flags.reset(DevicePaused);
3366 if(dev->mContexts.load()->empty())
3367 return;
3369 try {
3370 auto backend = dev->Backend.get();
3371 backend->start();
3372 dev->mDeviceState = DeviceState::Playing;
3374 catch(al::backend_exception& e) {
3375 ERR("%s\n", e.what());
3376 dev->handleDisconnect("%s", e.what());
3377 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3378 return;
3380 TRACE("Post-resume: %s, %s, %uhz, %u / %u buffer\n",
3381 DevFmtChannelsString(dev->FmtChans), DevFmtTypeString(dev->FmtType),
3382 dev->Frequency, dev->UpdateSize, dev->BufferSize);
3386 /************************************************
3387 * ALC HRTF functions
3388 ************************************************/
3390 /** Gets a string parameter at the given index. */
3391 ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index) noexcept
3393 DeviceRef dev{VerifyDevice(device)};
3394 if(!dev || dev->Type == DeviceType::Capture)
3395 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3396 else switch(paramName)
3398 case ALC_HRTF_SPECIFIER_SOFT:
3399 if(index >= 0 && static_cast<uint>(index) < dev->mHrtfList.size())
3400 return dev->mHrtfList[static_cast<uint>(index)].c_str();
3401 alcSetError(dev.get(), ALC_INVALID_VALUE);
3402 break;
3404 default:
3405 alcSetError(dev.get(), ALC_INVALID_ENUM);
3406 break;
3409 return nullptr;
3412 /** Resets the given device output, using the specified attribute list. */
3413 ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs) noexcept
3415 std::unique_lock<std::recursive_mutex> listlock{ListLock};
3416 DeviceRef dev{VerifyDevice(device)};
3417 if(!dev || dev->Type == DeviceType::Capture)
3419 listlock.unlock();
3420 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3421 return ALC_FALSE;
3423 std::lock_guard<std::mutex> statelock{dev->StateLock};
3424 listlock.unlock();
3426 /* Force the backend to stop mixing first since we're resetting. Also reset
3427 * the connected state so lost devices can attempt recover.
3429 if(dev->mDeviceState == DeviceState::Playing)
3431 dev->Backend->stop();
3432 dev->mDeviceState = DeviceState::Configured;
3435 return ResetDeviceParams(dev.get(), SpanFromAttributeList(attribs)) ? ALC_TRUE : ALC_FALSE;
3439 /************************************************
3440 * ALC device reopen functions
3441 ************************************************/
3443 /** Reopens the given device output, using the specified name and attribute list. */
3444 FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device,
3445 const ALCchar *deviceName, const ALCint *attribs) noexcept
3447 std::unique_lock<std::recursive_mutex> listlock{ListLock};
3448 DeviceRef dev{VerifyDevice(device)};
3449 if(!dev || dev->Type != DeviceType::Playback)
3451 listlock.unlock();
3452 alcSetError(dev.get(), ALC_INVALID_DEVICE);
3453 return ALC_FALSE;
3455 std::lock_guard<std::mutex> statelock{dev->StateLock};
3457 std::string_view devname{deviceName ? deviceName : ""};
3458 if(!devname.empty())
3460 if(devname.length() >= size_t{std::numeric_limits<int>::max()})
3462 ERR("Device name too long (%zu >= %d)\n", devname.length(),
3463 std::numeric_limits<int>::max());
3464 alcSetError(dev.get(), ALC_INVALID_VALUE);
3465 return ALC_FALSE;
3467 if(al::case_compare(devname, GetDefaultName()) == 0)
3468 devname = {};
3471 /* Force the backend device to stop first since we're opening another one. */
3472 const bool wasPlaying{dev->mDeviceState == DeviceState::Playing};
3473 if(wasPlaying)
3475 dev->Backend->stop();
3476 dev->mDeviceState = DeviceState::Configured;
3479 BackendPtr newbackend;
3480 try {
3481 newbackend = PlaybackFactory->createBackend(dev.get(), BackendType::Playback);
3482 newbackend->open(devname);
3484 catch(al::backend_exception &e) {
3485 listlock.unlock();
3486 newbackend = nullptr;
3488 WARN("Failed to reopen playback device: %s\n", e.what());
3489 alcSetError(dev.get(), (e.errorCode() == al::backend_error::OutOfMemory)
3490 ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE);
3492 if(dev->Connected.load(std::memory_order_relaxed) && wasPlaying)
3494 try {
3495 auto backend = dev->Backend.get();
3496 backend->start();
3497 dev->mDeviceState = DeviceState::Playing;
3499 catch(al::backend_exception &be) {
3500 ERR("%s\n", be.what());
3501 dev->handleDisconnect("%s", be.what());
3504 return ALC_FALSE;
3506 listlock.unlock();
3507 dev->Backend = std::move(newbackend);
3508 dev->mDeviceState = DeviceState::Unprepared;
3509 TRACE("Reopened device %p, \"%s\"\n", voidp{dev.get()}, dev->DeviceName.c_str());
3511 /* Always return true even if resetting fails. It shouldn't fail, but this
3512 * is primarily to avoid confusion by the app seeing the function return
3513 * false while the device is on the new output anyway. We could try to
3514 * restore the old backend if this fails, but the configuration would be
3515 * changed with the new backend and would need to be reset again with the
3516 * old one, and the provided attributes may not be appropriate or desirable
3517 * for the old device.
3519 * In this way, we essentially act as if the function succeeded, but
3520 * immediately disconnects following it.
3522 ResetDeviceParams(dev.get(), SpanFromAttributeList(attribs));
3523 return ALC_TRUE;
3526 /************************************************
3527 * ALC event query functions
3528 ************************************************/
3530 FORCE_ALIGN ALCenum ALC_APIENTRY alcEventIsSupportedSOFT(ALCenum eventType, ALCenum deviceType) noexcept
3532 auto etype = alc::GetEventType(eventType);
3533 if(!etype)
3535 WARN("Invalid event type: 0x%04x\n", eventType);
3536 alcSetError(nullptr, ALC_INVALID_ENUM);
3537 return ALC_EVENT_NOT_SUPPORTED_SOFT;
3540 auto supported = alc::EventSupport::NoSupport;
3541 switch(deviceType)
3543 case ALC_PLAYBACK_DEVICE_SOFT:
3544 if(PlaybackFactory)
3545 supported = PlaybackFactory->queryEventSupport(*etype, BackendType::Playback);
3546 break;
3548 case ALC_CAPTURE_DEVICE_SOFT:
3549 if(CaptureFactory)
3550 supported = CaptureFactory->queryEventSupport(*etype, BackendType::Capture);
3551 break;
3553 default:
3554 WARN("Invalid device type: 0x%04x\n", deviceType);
3555 alcSetError(nullptr, ALC_INVALID_ENUM);
3557 return al::to_underlying(supported);