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
26 #define WIN32_LEAN_AND_MEAN
55 #include <string_view>
65 #include "al/auxeffectslot.h"
66 #include "al/buffer.h"
68 #include "al/effect.h"
69 #include "al/filter.h"
70 #include "al/source.h"
71 #include "alc/events.h"
75 #include "alnumbers.h"
76 #include "alnumeric.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"
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"
112 #include "backends/pipewire.h"
115 #include "backends/jack.h"
117 #ifdef HAVE_PULSEAUDIO
118 #include "backends/pulseaudio.h"
121 #include "backends/alsa.h"
124 #include "backends/wasapi.h"
126 #ifdef HAVE_COREAUDIO
127 #include "backends/coreaudio.h"
130 #include "backends/opensl.h"
133 #include "backends/oboe.h"
136 #include "backends/solaris.h"
139 #include "backends/sndio.h"
142 #include "backends/oss.h"
145 #include "backends/dsound.h"
148 #include "backends/winmm.h"
150 #ifdef HAVE_PORTAUDIO
151 #include "backends/portaudio.h"
154 #include "backends/sdl2.h"
157 #include "backends/otherio.h"
160 #include "backends/wave.h"
164 #include "al/eax/api.h"
165 #include "al/eax/globals.h"
169 /************************************************
170 * Library initialization
171 ************************************************/
172 #if defined(_WIN32) && !defined(AL_LIBTYPE_STATIC)
173 BOOL APIENTRY
DllMain(HINSTANCE module
, DWORD reason
, LPVOID
/*reserved*/)
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
);
189 using namespace std::string_view_literals
;
190 using std::chrono::seconds
;
191 using std::chrono::nanoseconds
;
194 using float2
= std::array
<float,2>;
197 auto gProcessRunning
= true;
198 struct ProcessWatcher
{
199 ProcessWatcher() = default;
200 ProcessWatcher(const ProcessWatcher
&) = delete;
201 ProcessWatcher
& operator=(const ProcessWatcher
&) = delete;
202 ~ProcessWatcher() { gProcessRunning
= false; }
204 ProcessWatcher gProcessWatcher
;
206 /************************************************
208 ************************************************/
211 BackendFactory
& (*getFactory
)();
214 std::array BackendList
{
216 BackendInfo
{"pipewire", PipeWireBackendFactory::getFactory
},
218 #ifdef HAVE_PULSEAUDIO
219 BackendInfo
{"pulse", PulseBackendFactory::getFactory
},
222 BackendInfo
{"wasapi", WasapiBackendFactory::getFactory
},
224 #ifdef HAVE_COREAUDIO
225 BackendInfo
{"core", CoreAudioBackendFactory::getFactory
},
228 BackendInfo
{"oboe", OboeBackendFactory::getFactory
},
231 BackendInfo
{"opensl", OSLBackendFactory::getFactory
},
234 BackendInfo
{"alsa", AlsaBackendFactory::getFactory
},
237 BackendInfo
{"solaris", SolarisBackendFactory::getFactory
},
240 BackendInfo
{"sndio", SndIOBackendFactory::getFactory
},
243 BackendInfo
{"oss", OSSBackendFactory::getFactory
},
246 BackendInfo
{"jack", JackBackendFactory::getFactory
},
249 BackendInfo
{"dsound", DSoundBackendFactory::getFactory
},
252 BackendInfo
{"winmm", WinMMBackendFactory::getFactory
},
254 #ifdef HAVE_PORTAUDIO
255 BackendInfo
{"port", PortBackendFactory::getFactory
},
258 BackendInfo
{"sdl2", SDL2BackendFactory::getFactory
},
261 BackendInfo
{"otherio", OtherIOBackendFactory::getFactory
},
264 BackendInfo
{"null", NullBackendFactory::getFactory
},
266 BackendInfo
{"wave", WaveBackendFactory::getFactory
},
270 BackendFactory
*PlaybackFactory
{};
271 BackendFactory
*CaptureFactory
{};
274 [[nodiscard
]] constexpr auto GetNoErrorString() noexcept
{ return "No Error"; }
275 [[nodiscard
]] constexpr auto GetInvalidDeviceString() noexcept
{ return "Invalid Device"; }
276 [[nodiscard
]] constexpr auto GetInvalidContextString() noexcept
{ return "Invalid Context"; }
277 [[nodiscard
]] constexpr auto GetInvalidEnumString() noexcept
{ return "Invalid Enum"; }
278 [[nodiscard
]] constexpr auto GetInvalidValueString() noexcept
{ return "Invalid Value"; }
279 [[nodiscard
]] constexpr auto GetOutOfMemoryString() noexcept
{ return "Out of Memory"; }
281 [[nodiscard
]] constexpr auto GetDefaultName() noexcept
{ return "OpenAL Soft\0"; }
284 [[nodiscard
]] constexpr auto GetDevicePrefix() noexcept
{ return "OpenAL Soft on "sv
; }
286 [[nodiscard
]] constexpr auto GetDevicePrefix() noexcept
{ return std::string_view
{}; }
289 /************************************************
291 ************************************************/
293 /* Enumerated device names */
294 std::vector
<std::string
> alcAllDevicesArray
;
295 std::vector
<std::string
> alcCaptureDeviceArray
;
296 std::string alcAllDevicesList
;
297 std::string alcCaptureDeviceList
;
299 /* Default is always the first in the list */
300 std::string alcDefaultAllDevicesSpecifier
;
301 std::string alcCaptureDefaultDeviceSpecifier
;
303 std::atomic
<ALCenum
> LastNullDeviceError
{ALC_NO_ERROR
};
305 /* Flag to trap ALC device errors */
306 bool TrapALCError
{false};
308 /* One-time configuration init control */
309 std::once_flag alc_config_once
{};
311 /* Flag to specify if alcSuspendContext/alcProcessContext should defer/process
314 bool SuspendDefers
{true};
316 /* Initial seed for dithering. */
317 constexpr uint DitherRNGSeed
{22222u};
320 /************************************************
322 ************************************************/
323 [[nodiscard
]] constexpr auto GetNoDeviceExtList() noexcept
-> const char*
325 return "ALC_ENUMERATE_ALL_EXT "
326 "ALC_ENUMERATION_EXT "
328 "ALC_EXT_direct_context "
330 "ALC_EXT_thread_local_context "
332 "ALC_SOFT_loopback_bformat "
333 "ALC_SOFT_reopen_device "
334 "ALC_SOFT_system_events";
336 [[nodiscard
]] constexpr auto GetExtensionList() noexcept
-> const char*
338 return "ALC_ENUMERATE_ALL_EXT "
339 "ALC_ENUMERATION_EXT "
343 "ALC_EXT_direct_context "
344 "ALC_EXT_disconnect "
346 "ALC_EXT_thread_local_context "
347 "ALC_SOFT_device_clock "
350 "ALC_SOFT_loopback_bformat "
351 "ALC_SOFT_output_limiter "
352 "ALC_SOFT_output_mode "
353 "ALC_SOFT_pause_device "
354 "ALC_SOFT_reopen_device "
355 "ALC_SOFT_system_events";
358 constexpr int alcMajorVersion
{1};
359 constexpr int alcMinorVersion
{1};
361 constexpr int alcEFXMajorVersion
{1};
362 constexpr int alcEFXMinorVersion
{0};
365 using DeviceRef
= al::intrusive_ptr
<ALCdevice
>;
368 /************************************************
370 ************************************************/
371 std::vector
<ALCdevice
*> DeviceList
;
372 std::vector
<ALCcontext
*> ContextList
;
374 std::recursive_mutex ListLock
;
377 void alc_initconfig()
379 if(auto loglevel
= al::getenv("ALSOFT_LOGLEVEL"))
381 long lvl
= strtol(loglevel
->c_str(), nullptr, 0);
382 if(lvl
>= static_cast<long>(LogLevel::Trace
))
383 gLogLevel
= LogLevel::Trace
;
384 else if(lvl
<= static_cast<long>(LogLevel::Disable
))
385 gLogLevel
= LogLevel::Disable
;
387 gLogLevel
= static_cast<LogLevel
>(lvl
);
391 if(const auto logfile
= al::getenv(L
"ALSOFT_LOGFILE"))
393 FILE *logf
{_wfopen(logfile
->c_str(), L
"wt")};
394 if(logf
) gLogFile
= logf
;
397 auto u8name
= wstr_to_utf8(*logfile
);
398 ERR("Failed to open log file '%s'\n", u8name
.c_str());
402 if(const auto logfile
= al::getenv("ALSOFT_LOGFILE"))
404 FILE *logf
{fopen(logfile
->c_str(), "wt")};
405 if(logf
) gLogFile
= logf
;
406 else ERR("Failed to open log file '%s'\n", logfile
->c_str());
410 TRACE("Initializing library v%s-%s %s\n", ALSOFT_VERSION
, ALSOFT_GIT_COMMIT_HASH
,
414 if(std::size(BackendList
) < 1)
418 const al::span
<const BackendInfo
> infos
{BackendList
};
419 names
= infos
[0].name
;
420 for(const auto &backend
: infos
.subspan
<1>())
423 names
+= backend
.name
;
426 TRACE("Supported backends: %s\n", names
.c_str());
430 if(auto suspendmode
= al::getenv("__ALSOFT_SUSPEND_CONTEXT"))
432 if(al::case_compare(*suspendmode
, "ignore"sv
) == 0)
434 SuspendDefers
= false;
435 TRACE("Selected context suspend behavior, \"ignore\"\n");
438 ERR("Unhandled context suspend behavior setting: \"%s\"\n", suspendmode
->c_str());
442 #if defined(HAVE_SSE4_1)
443 capfilter
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
| CPU_CAP_SSE4_1
;
444 #elif defined(HAVE_SSE3)
445 capfilter
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
;
446 #elif defined(HAVE_SSE2)
447 capfilter
|= CPU_CAP_SSE
| CPU_CAP_SSE2
;
448 #elif defined(HAVE_SSE)
449 capfilter
|= CPU_CAP_SSE
;
452 capfilter
|= CPU_CAP_NEON
;
454 if(auto cpuopt
= ConfigValueStr({}, {}, "disable-cpu-exts"sv
))
456 std::string_view cpulist
{*cpuopt
};
457 if(al::case_compare(cpulist
, "all"sv
) == 0)
459 else while(!cpulist
.empty())
461 auto nextpos
= std::min(cpulist
.find(','), cpulist
.size());
462 auto entry
= cpulist
.substr(0, nextpos
);
464 while(nextpos
< cpulist
.size() && cpulist
[nextpos
] == ',')
466 cpulist
.remove_prefix(nextpos
);
468 while(!entry
.empty() && std::isspace(entry
.front()))
469 entry
.remove_prefix(1);
470 while(!entry
.empty() && std::isspace(entry
.back()))
471 entry
.remove_suffix(1);
475 if(al::case_compare(entry
, "sse"sv
) == 0)
476 capfilter
&= ~CPU_CAP_SSE
;
477 else if(al::case_compare(entry
, "sse2"sv
) == 0)
478 capfilter
&= ~CPU_CAP_SSE2
;
479 else if(al::case_compare(entry
, "sse3"sv
) == 0)
480 capfilter
&= ~CPU_CAP_SSE3
;
481 else if(al::case_compare(entry
, "sse4.1"sv
) == 0)
482 capfilter
&= ~CPU_CAP_SSE4_1
;
483 else if(al::case_compare(entry
, "neon"sv
) == 0)
484 capfilter
&= ~CPU_CAP_NEON
;
486 WARN("Invalid CPU extension \"%.*s\"\n", al::sizei(entry
), entry
.data());
489 if(auto cpuopt
= GetCPUInfo())
491 if(!cpuopt
->mVendor
.empty() || !cpuopt
->mName
.empty())
493 TRACE("Vendor ID: \"%s\"\n", cpuopt
->mVendor
.c_str());
494 TRACE("Name: \"%s\"\n", cpuopt
->mName
.c_str());
496 const int caps
{cpuopt
->mCaps
};
497 TRACE("Extensions:%s%s%s%s%s%s\n",
498 ((capfilter
&CPU_CAP_SSE
) ? ((caps
&CPU_CAP_SSE
) ? " +SSE" : " -SSE") : ""),
499 ((capfilter
&CPU_CAP_SSE2
) ? ((caps
&CPU_CAP_SSE2
) ? " +SSE2" : " -SSE2") : ""),
500 ((capfilter
&CPU_CAP_SSE3
) ? ((caps
&CPU_CAP_SSE3
) ? " +SSE3" : " -SSE3") : ""),
501 ((capfilter
&CPU_CAP_SSE4_1
) ? ((caps
&CPU_CAP_SSE4_1
) ? " +SSE4.1" : " -SSE4.1") : ""),
502 ((capfilter
&CPU_CAP_NEON
) ? ((caps
&CPU_CAP_NEON
) ? " +NEON" : " -NEON") : ""),
503 ((!capfilter
) ? " -none-" : ""));
504 CPUCapFlags
= caps
& capfilter
;
507 if(auto priopt
= ConfigValueInt({}, {}, "rt-prio"sv
))
508 RTPrioLevel
= *priopt
;
509 if(auto limopt
= ConfigValueBool({}, {}, "rt-time-limit"sv
))
510 AllowRTTimeLimit
= *limopt
;
513 CompatFlagBitset compatflags
{};
514 auto checkflag
= [](const char *envname
, const std::string_view optname
) -> bool
516 if(auto optval
= al::getenv(envname
))
518 return al::case_compare(*optval
, "true"sv
) == 0
519 || strtol(optval
->c_str(), nullptr, 0) == 1;
521 return GetConfigValueBool({}, "game_compat", optname
, false);
523 sBufferSubDataCompat
= checkflag("__ALSOFT_ENABLE_SUB_DATA_EXT", "enable-sub-data-ext"sv
);
524 compatflags
.set(CompatFlags::ReverseX
, checkflag("__ALSOFT_REVERSE_X", "reverse-x"sv
));
525 compatflags
.set(CompatFlags::ReverseY
, checkflag("__ALSOFT_REVERSE_Y", "reverse-y"sv
));
526 compatflags
.set(CompatFlags::ReverseZ
, checkflag("__ALSOFT_REVERSE_Z", "reverse-z"sv
));
528 aluInit(compatflags
, ConfigValueFloat({}, "game_compat"sv
, "nfc-scale"sv
).value_or(1.0f
));
530 Voice::InitMixer(ConfigValueStr({}, {}, "resampler"sv
));
532 if(auto uhjfiltopt
= ConfigValueStr({}, "uhj"sv
, "decode-filter"sv
))
534 if(al::case_compare(*uhjfiltopt
, "fir256"sv
) == 0)
535 UhjDecodeQuality
= UhjQualityType::FIR256
;
536 else if(al::case_compare(*uhjfiltopt
, "fir512"sv
) == 0)
537 UhjDecodeQuality
= UhjQualityType::FIR512
;
538 else if(al::case_compare(*uhjfiltopt
, "iir"sv
) == 0)
539 UhjDecodeQuality
= UhjQualityType::IIR
;
541 WARN("Unsupported uhj/decode-filter: %s\n", uhjfiltopt
->c_str());
543 if(auto uhjfiltopt
= ConfigValueStr({}, "uhj"sv
, "encode-filter"sv
))
545 if(al::case_compare(*uhjfiltopt
, "fir256"sv
) == 0)
546 UhjEncodeQuality
= UhjQualityType::FIR256
;
547 else if(al::case_compare(*uhjfiltopt
, "fir512"sv
) == 0)
548 UhjEncodeQuality
= UhjQualityType::FIR512
;
549 else if(al::case_compare(*uhjfiltopt
, "iir"sv
) == 0)
550 UhjEncodeQuality
= UhjQualityType::IIR
;
552 WARN("Unsupported uhj/encode-filter: %s\n", uhjfiltopt
->c_str());
555 if(auto traperr
= al::getenv("ALSOFT_TRAP_ERROR"); traperr
556 && (al::case_compare(*traperr
, "true"sv
) == 0
557 || std::strtol(traperr
->c_str(), nullptr, 0) == 1))
564 traperr
= al::getenv("ALSOFT_TRAP_AL_ERROR");
566 TrapALError
= al::case_compare(*traperr
, "true"sv
) == 0
567 || strtol(traperr
->c_str(), nullptr, 0) == 1;
569 TrapALError
= GetConfigValueBool({}, {}, "trap-al-error"sv
, false);
571 traperr
= al::getenv("ALSOFT_TRAP_ALC_ERROR");
573 TrapALCError
= al::case_compare(*traperr
, "true"sv
) == 0
574 || strtol(traperr
->c_str(), nullptr, 0) == 1;
576 TrapALCError
= GetConfigValueBool({}, {}, "trap-alc-error"sv
, false);
579 if(auto boostopt
= ConfigValueFloat({}, "reverb"sv
, "boost"sv
))
581 const float valf
{std::isfinite(*boostopt
) ? std::clamp(*boostopt
, -24.0f
, 24.0f
) : 0.0f
};
582 ReverbBoost
*= std::pow(10.0f
, valf
/ 20.0f
);
585 auto BackendListEnd
= BackendList
.end();
586 auto devopt
= al::getenv("ALSOFT_DRIVERS");
587 if(!devopt
) devopt
= ConfigValueStr({}, {}, "drivers"sv
);
590 auto backendlist_cur
= BackendList
.begin();
593 std::string_view drvlist
{*devopt
};
594 while(!drvlist
.empty())
596 auto nextpos
= std::min(drvlist
.find(','), drvlist
.size());
597 auto entry
= drvlist
.substr(0, nextpos
);
600 if(nextpos
< drvlist
.size())
603 while(nextpos
< drvlist
.size() && drvlist
[nextpos
] == ',')
606 drvlist
.remove_prefix(nextpos
);
608 while(!entry
.empty() && std::isspace(entry
.front()))
609 entry
.remove_prefix(1);
610 const bool delitem
{!entry
.empty() && entry
.front() == '-'};
611 if(delitem
) entry
.remove_prefix(1);
613 while(!entry
.empty() && std::isspace(entry
.back()))
614 entry
.remove_suffix(1);
619 /* HACK: For backwards compatibility, convert backend references of
620 * mmdevapi to wasapi. This should eventually be removed.
622 if(entry
== "mmdevapi"sv
)
626 auto find_backend
= [entry
](const BackendInfo
&backend
) -> bool
627 { return entry
== backend
.name
; };
628 auto this_backend
= std::find_if(BackendList
.begin(), BackendListEnd
, find_backend
);
630 if(this_backend
== BackendListEnd
)
634 BackendListEnd
= std::move(this_backend
+1, BackendListEnd
, this_backend
);
636 backendlist_cur
= std::rotate(backendlist_cur
, this_backend
, this_backend
+1);
640 BackendListEnd
= backendlist_cur
;
643 auto init_backend
= [](BackendInfo
&backend
) -> void
645 if(PlaybackFactory
&& CaptureFactory
)
648 BackendFactory
&factory
= backend
.getFactory();
651 WARN("Failed to initialize backend \"%s\"\n", backend
.name
);
655 TRACE("Initialized backend \"%s\"\n", backend
.name
);
656 if(!PlaybackFactory
&& factory
.querySupport(BackendType::Playback
))
658 PlaybackFactory
= &factory
;
659 TRACE("Added \"%s\" for playback\n", backend
.name
);
661 if(!CaptureFactory
&& factory
.querySupport(BackendType::Capture
))
663 CaptureFactory
= &factory
;
664 TRACE("Added \"%s\" for capture\n", backend
.name
);
667 std::for_each(BackendList
.begin(), BackendListEnd
, init_backend
);
669 LoopbackBackendFactory::getFactory().init();
672 WARN("No playback backend available!\n");
674 WARN("No capture backend available!\n");
676 if(auto exclopt
= ConfigValueStr({}, {}, "excludefx"sv
))
678 std::string_view exclude
{*exclopt
};
679 while(!exclude
.empty())
681 const auto nextpos
= exclude
.find(',');
682 const auto entry
= exclude
.substr(0, nextpos
);
683 exclude
.remove_prefix((nextpos
< exclude
.size()) ? nextpos
+1 : exclude
.size());
685 std::for_each(gEffectList
.cbegin(), gEffectList
.cend(),
686 [entry
](const EffectList
&effectitem
) noexcept
688 if(entry
== std::data(effectitem
.name
))
689 DisabledEffects
.set(effectitem
.type
);
694 InitEffect(&ALCcontext::sDefaultEffect
);
695 auto defrevopt
= al::getenv("ALSOFT_DEFAULT_REVERB");
696 if(!defrevopt
) defrevopt
= ConfigValueStr({}, {}, "default-reverb"sv
);
697 if(defrevopt
) LoadReverbPreset(*defrevopt
, &ALCcontext::sDefaultEffect
);
701 if(const auto eax_enable_opt
= ConfigValueBool({}, "eax", "enable"))
703 eax_g_is_enabled
= *eax_enable_opt
;
704 if(!eax_g_is_enabled
)
705 TRACE("%s\n", "EAX disabled by a configuration.");
708 eax_g_is_enabled
= true;
710 if((DisabledEffects
.test(EAXREVERB_EFFECT
) || DisabledEffects
.test(CHORUS_EFFECT
))
713 eax_g_is_enabled
= false;
714 TRACE("EAX disabled because %s disabled.\n",
715 (DisabledEffects
.test(EAXREVERB_EFFECT
) && DisabledEffects
.test(CHORUS_EFFECT
))
716 ? "EAXReverb and Chorus are" :
717 DisabledEffects
.test(EAXREVERB_EFFECT
) ? "EAXReverb is" :
718 DisabledEffects
.test(CHORUS_EFFECT
) ? "Chorus is" : "");
723 inline void InitConfig()
724 { std::call_once(alc_config_once
, [](){alc_initconfig();}); }
727 /************************************************
729 ************************************************/
730 void ProbeAllDevicesList()
734 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
737 decltype(alcAllDevicesArray
){}.swap(alcAllDevicesArray
);
738 decltype(alcAllDevicesList
){}.swap(alcAllDevicesList
);
742 alcAllDevicesArray
= PlaybackFactory
->enumerate(BackendType::Playback
);
743 if(const auto prefix
= GetDevicePrefix(); !prefix
.empty())
744 std::for_each(alcAllDevicesArray
.begin(), alcAllDevicesArray
.end(),
745 [prefix
](std::string
&name
) { name
.insert(0, prefix
); });
747 decltype(alcAllDevicesList
){}.swap(alcAllDevicesList
);
748 if(alcAllDevicesArray
.empty())
749 alcAllDevicesList
+= '\0';
750 else for(auto &devname
: alcAllDevicesArray
)
751 alcAllDevicesList
.append(devname
) += '\0';
754 void ProbeCaptureDeviceList()
758 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
761 decltype(alcCaptureDeviceArray
){}.swap(alcCaptureDeviceArray
);
762 decltype(alcCaptureDeviceList
){}.swap(alcCaptureDeviceList
);
766 alcCaptureDeviceArray
= CaptureFactory
->enumerate(BackendType::Capture
);
767 if(const auto prefix
= GetDevicePrefix(); !prefix
.empty())
768 std::for_each(alcCaptureDeviceArray
.begin(), alcCaptureDeviceArray
.end(),
769 [prefix
](std::string
&name
) { name
.insert(0, prefix
); });
771 decltype(alcCaptureDeviceList
){}.swap(alcCaptureDeviceList
);
772 if(alcCaptureDeviceArray
.empty())
773 alcCaptureDeviceList
+= '\0';
774 else for(auto &devname
: alcCaptureDeviceArray
)
775 alcCaptureDeviceList
.append(devname
) += '\0';
780 al::span
<const ALCint
> SpanFromAttributeList(const ALCint
*attribs
) noexcept
782 al::span
<const ALCint
> attrSpan
;
785 const ALCint
*attrEnd
{attribs
};
787 attrEnd
+= 2; /* NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) */
788 attrSpan
= {attribs
, attrEnd
};
793 struct DevFmtPair
{ DevFmtChannels chans
; DevFmtType type
; };
794 std::optional
<DevFmtPair
> DecomposeDevFormat(ALenum format
)
798 DevFmtChannels channels
;
801 static constexpr std::array list
{
802 FormatType
{AL_FORMAT_MONO8
, DevFmtMono
, DevFmtUByte
},
803 FormatType
{AL_FORMAT_MONO16
, DevFmtMono
, DevFmtShort
},
804 FormatType
{AL_FORMAT_MONO_I32
, DevFmtMono
, DevFmtInt
},
805 FormatType
{AL_FORMAT_MONO_FLOAT32
, DevFmtMono
, DevFmtFloat
},
807 FormatType
{AL_FORMAT_STEREO8
, DevFmtStereo
, DevFmtUByte
},
808 FormatType
{AL_FORMAT_STEREO16
, DevFmtStereo
, DevFmtShort
},
809 FormatType
{AL_FORMAT_STEREO_I32
, DevFmtStereo
, DevFmtInt
},
810 FormatType
{AL_FORMAT_STEREO_FLOAT32
, DevFmtStereo
, DevFmtFloat
},
812 FormatType
{AL_FORMAT_QUAD8
, DevFmtQuad
, DevFmtUByte
},
813 FormatType
{AL_FORMAT_QUAD16
, DevFmtQuad
, DevFmtShort
},
814 FormatType
{AL_FORMAT_QUAD32
, DevFmtQuad
, DevFmtFloat
},
815 FormatType
{AL_FORMAT_QUAD_I32
, DevFmtQuad
, DevFmtInt
},
816 FormatType
{AL_FORMAT_QUAD_FLOAT32
, DevFmtQuad
, DevFmtFloat
},
818 FormatType
{AL_FORMAT_51CHN8
, DevFmtX51
, DevFmtUByte
},
819 FormatType
{AL_FORMAT_51CHN16
, DevFmtX51
, DevFmtShort
},
820 FormatType
{AL_FORMAT_51CHN32
, DevFmtX51
, DevFmtFloat
},
821 FormatType
{AL_FORMAT_51CHN_I32
, DevFmtX51
, DevFmtInt
},
822 FormatType
{AL_FORMAT_51CHN_FLOAT32
, DevFmtX51
, DevFmtFloat
},
824 FormatType
{AL_FORMAT_61CHN8
, DevFmtX61
, DevFmtUByte
},
825 FormatType
{AL_FORMAT_61CHN16
, DevFmtX61
, DevFmtShort
},
826 FormatType
{AL_FORMAT_61CHN32
, DevFmtX61
, DevFmtFloat
},
827 FormatType
{AL_FORMAT_61CHN_I32
, DevFmtX61
, DevFmtInt
},
828 FormatType
{AL_FORMAT_61CHN_FLOAT32
, DevFmtX61
, DevFmtFloat
},
830 FormatType
{AL_FORMAT_71CHN8
, DevFmtX71
, DevFmtUByte
},
831 FormatType
{AL_FORMAT_71CHN16
, DevFmtX71
, DevFmtShort
},
832 FormatType
{AL_FORMAT_71CHN32
, DevFmtX71
, DevFmtFloat
},
833 FormatType
{AL_FORMAT_71CHN_I32
, DevFmtX71
, DevFmtInt
},
834 FormatType
{AL_FORMAT_71CHN_FLOAT32
, DevFmtX71
, DevFmtFloat
},
837 for(const auto &item
: list
)
839 if(item
.format
== format
)
840 return DevFmtPair
{item
.channels
, item
.type
};
846 std::optional
<DevFmtType
> DevFmtTypeFromEnum(ALCenum type
)
850 case ALC_BYTE_SOFT
: return DevFmtByte
;
851 case ALC_UNSIGNED_BYTE_SOFT
: return DevFmtUByte
;
852 case ALC_SHORT_SOFT
: return DevFmtShort
;
853 case ALC_UNSIGNED_SHORT_SOFT
: return DevFmtUShort
;
854 case ALC_INT_SOFT
: return DevFmtInt
;
855 case ALC_UNSIGNED_INT_SOFT
: return DevFmtUInt
;
856 case ALC_FLOAT_SOFT
: return DevFmtFloat
;
858 WARN("Unsupported format type: 0x%04x\n", type
);
861 ALCenum
EnumFromDevFmt(DevFmtType type
)
865 case DevFmtByte
: return ALC_BYTE_SOFT
;
866 case DevFmtUByte
: return ALC_UNSIGNED_BYTE_SOFT
;
867 case DevFmtShort
: return ALC_SHORT_SOFT
;
868 case DevFmtUShort
: return ALC_UNSIGNED_SHORT_SOFT
;
869 case DevFmtInt
: return ALC_INT_SOFT
;
870 case DevFmtUInt
: return ALC_UNSIGNED_INT_SOFT
;
871 case DevFmtFloat
: return ALC_FLOAT_SOFT
;
873 throw std::runtime_error
{"Invalid DevFmtType: "+std::to_string(int(type
))};
876 std::optional
<DevFmtChannels
> DevFmtChannelsFromEnum(ALCenum channels
)
880 case ALC_MONO_SOFT
: return DevFmtMono
;
881 case ALC_STEREO_SOFT
: return DevFmtStereo
;
882 case ALC_QUAD_SOFT
: return DevFmtQuad
;
883 case ALC_5POINT1_SOFT
: return DevFmtX51
;
884 case ALC_6POINT1_SOFT
: return DevFmtX61
;
885 case ALC_7POINT1_SOFT
: return DevFmtX71
;
886 case ALC_BFORMAT3D_SOFT
: return DevFmtAmbi3D
;
888 WARN("Unsupported format channels: 0x%04x\n", channels
);
891 ALCenum
EnumFromDevFmt(DevFmtChannels channels
)
895 case DevFmtMono
: return ALC_MONO_SOFT
;
896 case DevFmtStereo
: return ALC_STEREO_SOFT
;
897 case DevFmtQuad
: return ALC_QUAD_SOFT
;
898 case DevFmtX51
: return ALC_5POINT1_SOFT
;
899 case DevFmtX61
: return ALC_6POINT1_SOFT
;
900 case DevFmtX71
: return ALC_7POINT1_SOFT
;
901 case DevFmtAmbi3D
: return ALC_BFORMAT3D_SOFT
;
902 /* FIXME: Shouldn't happen. */
905 case DevFmtX3D71
: break;
907 throw std::runtime_error
{"Invalid DevFmtChannels: "+std::to_string(int(channels
))};
910 std::optional
<DevAmbiLayout
> DevAmbiLayoutFromEnum(ALCenum layout
)
914 case ALC_FUMA_SOFT
: return DevAmbiLayout::FuMa
;
915 case ALC_ACN_SOFT
: return DevAmbiLayout::ACN
;
917 WARN("Unsupported ambisonic layout: 0x%04x\n", layout
);
920 ALCenum
EnumFromDevAmbi(DevAmbiLayout layout
)
924 case DevAmbiLayout::FuMa
: return ALC_FUMA_SOFT
;
925 case DevAmbiLayout::ACN
: return ALC_ACN_SOFT
;
927 throw std::runtime_error
{"Invalid DevAmbiLayout: "+std::to_string(int(layout
))};
930 std::optional
<DevAmbiScaling
> DevAmbiScalingFromEnum(ALCenum scaling
)
934 case ALC_FUMA_SOFT
: return DevAmbiScaling::FuMa
;
935 case ALC_SN3D_SOFT
: return DevAmbiScaling::SN3D
;
936 case ALC_N3D_SOFT
: return DevAmbiScaling::N3D
;
938 WARN("Unsupported ambisonic scaling: 0x%04x\n", scaling
);
941 ALCenum
EnumFromDevAmbi(DevAmbiScaling scaling
)
945 case DevAmbiScaling::FuMa
: return ALC_FUMA_SOFT
;
946 case DevAmbiScaling::SN3D
: return ALC_SN3D_SOFT
;
947 case DevAmbiScaling::N3D
: return ALC_N3D_SOFT
;
949 throw std::runtime_error
{"Invalid DevAmbiScaling: "+std::to_string(int(scaling
))};
953 /* Downmixing channel arrays, to map a device format's missing channels to
954 * existing ones. Based on what PipeWire does, though simplified.
956 constexpr float inv_sqrt2f
{static_cast<float>(1.0 / al::numbers::sqrt2
)};
957 constexpr std::array FrontStereo3dB
{
958 InputRemixMap::TargetMix
{FrontLeft
, inv_sqrt2f
},
959 InputRemixMap::TargetMix
{FrontRight
, inv_sqrt2f
}
961 constexpr std::array FrontStereo6dB
{
962 InputRemixMap::TargetMix
{FrontLeft
, 0.5f
},
963 InputRemixMap::TargetMix
{FrontRight
, 0.5f
}
965 constexpr std::array SideStereo3dB
{
966 InputRemixMap::TargetMix
{SideLeft
, inv_sqrt2f
},
967 InputRemixMap::TargetMix
{SideRight
, inv_sqrt2f
}
969 constexpr std::array BackStereo3dB
{
970 InputRemixMap::TargetMix
{BackLeft
, inv_sqrt2f
},
971 InputRemixMap::TargetMix
{BackRight
, inv_sqrt2f
}
973 constexpr std::array FrontLeft3dB
{InputRemixMap::TargetMix
{FrontLeft
, inv_sqrt2f
}};
974 constexpr std::array FrontRight3dB
{InputRemixMap::TargetMix
{FrontRight
, inv_sqrt2f
}};
975 constexpr std::array SideLeft0dB
{InputRemixMap::TargetMix
{SideLeft
, 1.0f
}};
976 constexpr std::array SideRight0dB
{InputRemixMap::TargetMix
{SideRight
, 1.0f
}};
977 constexpr std::array BackLeft0dB
{InputRemixMap::TargetMix
{BackLeft
, 1.0f
}};
978 constexpr std::array BackRight0dB
{InputRemixMap::TargetMix
{BackRight
, 1.0f
}};
979 constexpr std::array BackCenter3dB
{InputRemixMap::TargetMix
{BackCenter
, inv_sqrt2f
}};
981 constexpr std::array StereoDownmix
{
982 InputRemixMap
{FrontCenter
, FrontStereo3dB
},
983 InputRemixMap
{SideLeft
, FrontLeft3dB
},
984 InputRemixMap
{SideRight
, FrontRight3dB
},
985 InputRemixMap
{BackLeft
, FrontLeft3dB
},
986 InputRemixMap
{BackRight
, FrontRight3dB
},
987 InputRemixMap
{BackCenter
, FrontStereo6dB
},
989 constexpr std::array QuadDownmix
{
990 InputRemixMap
{FrontCenter
, FrontStereo3dB
},
991 InputRemixMap
{SideLeft
, BackLeft0dB
},
992 InputRemixMap
{SideRight
, BackRight0dB
},
993 InputRemixMap
{BackCenter
, BackStereo3dB
},
995 constexpr std::array X51Downmix
{
996 InputRemixMap
{BackLeft
, SideLeft0dB
},
997 InputRemixMap
{BackRight
, SideRight0dB
},
998 InputRemixMap
{BackCenter
, SideStereo3dB
},
1000 constexpr std::array X61Downmix
{
1001 InputRemixMap
{BackLeft
, BackCenter3dB
},
1002 InputRemixMap
{BackRight
, BackCenter3dB
},
1004 constexpr std::array X71Downmix
{
1005 InputRemixMap
{BackCenter
, BackStereo3dB
},
1009 std::unique_ptr
<Compressor
> CreateDeviceLimiter(const ALCdevice
*device
, const float threshold
)
1011 static constexpr bool AutoKnee
{true};
1012 static constexpr bool AutoAttack
{true};
1013 static constexpr bool AutoRelease
{true};
1014 static constexpr bool AutoPostGain
{true};
1015 static constexpr bool AutoDeclip
{true};
1016 static constexpr float LookAheadTime
{0.001f
};
1017 static constexpr float HoldTime
{0.002f
};
1018 static constexpr float PreGainDb
{0.0f
};
1019 static constexpr float PostGainDb
{0.0f
};
1020 static constexpr float Ratio
{std::numeric_limits
<float>::infinity()};
1021 static constexpr float KneeDb
{0.0f
};
1022 static constexpr float AttackTime
{0.02f
};
1023 static constexpr float ReleaseTime
{0.2f
};
1025 return Compressor::Create(device
->RealOut
.Buffer
.size(), static_cast<float>(device
->Frequency
),
1026 AutoKnee
, AutoAttack
, AutoRelease
, AutoPostGain
, AutoDeclip
, LookAheadTime
, HoldTime
,
1027 PreGainDb
, PostGainDb
, threshold
, Ratio
, KneeDb
, AttackTime
, ReleaseTime
);
1031 * Updates the device's base clock time with however many samples have been
1032 * done. This is used so frequency changes on the device don't cause the time
1033 * to jump forward or back. Must not be called while the device is running/
1036 inline void UpdateClockBase(ALCdevice
*device
)
1038 const auto mixLock
= device
->getWriteMixLock();
1040 auto samplesDone
= device
->mSamplesDone
.load(std::memory_order_relaxed
);
1041 auto clockBase
= device
->mClockBase
.load(std::memory_order_relaxed
);
1043 clockBase
+= nanoseconds
{seconds
{samplesDone
}} / device
->Frequency
;
1044 device
->mClockBase
.store(clockBase
, std::memory_order_relaxed
);
1045 device
->mSamplesDone
.store(0, std::memory_order_relaxed
);
1049 * Updates device parameters according to the attribute list (caller is
1050 * responsible for holding the list lock).
1052 ALCenum
UpdateDeviceParams(ALCdevice
*device
, const al::span
<const int> attrList
)
1054 if(attrList
.empty() && device
->Type
== DeviceType::Loopback
)
1056 WARN("Missing attributes for loopback device\n");
1057 return ALC_INVALID_VALUE
;
1060 uint numMono
{device
->NumMonoSources
};
1061 uint numStereo
{device
->NumStereoSources
};
1062 uint numSends
{device
->NumAuxSends
};
1063 std::optional
<StereoEncoding
> stereomode
;
1064 std::optional
<bool> optlimit
;
1065 std::optional
<uint
> optsrate
;
1066 std::optional
<DevFmtChannels
> optchans
;
1067 std::optional
<DevFmtType
> opttype
;
1068 std::optional
<DevAmbiLayout
> optlayout
;
1069 std::optional
<DevAmbiScaling
> optscale
;
1070 uint period_size
{DefaultUpdateSize
};
1071 uint buffer_size
{DefaultUpdateSize
* DefaultNumUpdates
};
1075 if(device
->Type
!= DeviceType::Loopback
)
1077 /* Get default settings from the user configuration */
1079 if(auto freqopt
= device
->configValue
<uint
>({}, "frequency"))
1081 optsrate
= std::clamp
<uint
>(*freqopt
, MinOutputRate
, MaxOutputRate
);
1083 const double scale
{static_cast<double>(*optsrate
) / double{DefaultOutputRate
}};
1084 period_size
= static_cast<uint
>(std::lround(period_size
* scale
));
1087 if(auto persizeopt
= device
->configValue
<uint
>({}, "period_size"))
1088 period_size
= std::clamp(*persizeopt
, 64u, 8192u);
1089 if(auto numperopt
= device
->configValue
<uint
>({}, "periods"))
1090 buffer_size
= std::clamp(*numperopt
, 2u, 16u) * period_size
;
1092 buffer_size
= period_size
* uint
{DefaultNumUpdates
};
1094 if(auto typeopt
= device
->configValue
<std::string
>({}, "sample-type"))
1097 std::string_view name
;
1100 constexpr std::array typelist
{
1101 TypeMap
{"int8"sv
, DevFmtByte
},
1102 TypeMap
{"uint8"sv
, DevFmtUByte
},
1103 TypeMap
{"int16"sv
, DevFmtShort
},
1104 TypeMap
{"uint16"sv
, DevFmtUShort
},
1105 TypeMap
{"int32"sv
, DevFmtInt
},
1106 TypeMap
{"uint32"sv
, DevFmtUInt
},
1107 TypeMap
{"float32"sv
, DevFmtFloat
},
1110 const ALCchar
*fmt
{typeopt
->c_str()};
1111 auto iter
= std::find_if(typelist
.begin(), typelist
.end(),
1112 [svfmt
=std::string_view
{fmt
}](const TypeMap
&entry
) -> bool
1113 { return al::case_compare(entry
.name
, svfmt
) == 0; });
1114 if(iter
== typelist
.end())
1115 ERR("Unsupported sample-type: %s\n", fmt
);
1117 opttype
= iter
->type
;
1119 if(auto chanopt
= device
->configValue
<std::string
>({}, "channels"))
1122 std::string_view name
;
1123 DevFmtChannels chans
;
1126 constexpr std::array chanlist
{
1127 ChannelMap
{"mono"sv
, DevFmtMono
, 0},
1128 ChannelMap
{"stereo"sv
, DevFmtStereo
, 0},
1129 ChannelMap
{"quad"sv
, DevFmtQuad
, 0},
1130 ChannelMap
{"surround51"sv
, DevFmtX51
, 0},
1131 ChannelMap
{"surround61"sv
, DevFmtX61
, 0},
1132 ChannelMap
{"surround71"sv
, DevFmtX71
, 0},
1133 ChannelMap
{"surround714"sv
, DevFmtX714
, 0},
1134 ChannelMap
{"surround7144"sv
, DevFmtX7144
, 0},
1135 ChannelMap
{"surround3d71"sv
, DevFmtX3D71
, 0},
1136 ChannelMap
{"surround51rear"sv
, DevFmtX51
, 0},
1137 ChannelMap
{"ambi1"sv
, DevFmtAmbi3D
, 1},
1138 ChannelMap
{"ambi2"sv
, DevFmtAmbi3D
, 2},
1139 ChannelMap
{"ambi3"sv
, DevFmtAmbi3D
, 3},
1142 const ALCchar
*fmt
{chanopt
->c_str()};
1143 auto iter
= std::find_if(chanlist
.begin(), chanlist
.end(),
1144 [svfmt
=std::string_view
{fmt
}](const ChannelMap
&entry
) -> bool
1145 { return al::case_compare(entry
.name
, svfmt
) == 0; });
1146 if(iter
== chanlist
.end())
1147 ERR("Unsupported channels: %s\n", fmt
);
1150 optchans
= iter
->chans
;
1151 aorder
= iter
->order
;
1154 if(auto ambiopt
= device
->configValue
<std::string
>({}, "ambi-format"sv
))
1156 if(al::case_compare(*ambiopt
, "fuma"sv
) == 0)
1158 optlayout
= DevAmbiLayout::FuMa
;
1159 optscale
= DevAmbiScaling::FuMa
;
1161 else if(al::case_compare(*ambiopt
, "acn+fuma"sv
) == 0)
1163 optlayout
= DevAmbiLayout::ACN
;
1164 optscale
= DevAmbiScaling::FuMa
;
1166 else if(al::case_compare(*ambiopt
, "ambix"sv
) == 0
1167 || al::case_compare(*ambiopt
, "acn+sn3d"sv
) == 0)
1169 optlayout
= DevAmbiLayout::ACN
;
1170 optscale
= DevAmbiScaling::SN3D
;
1172 else if(al::case_compare(*ambiopt
, "acn+n3d"sv
) == 0)
1174 optlayout
= DevAmbiLayout::ACN
;
1175 optscale
= DevAmbiScaling::N3D
;
1178 ERR("Unsupported ambi-format: %s\n", ambiopt
->c_str());
1181 if(auto hrtfopt
= device
->configValue
<std::string
>({}, "hrtf"sv
))
1183 WARN("general/hrtf is deprecated, please use stereo-encoding instead\n");
1185 if(al::case_compare(*hrtfopt
, "true"sv
) == 0)
1186 stereomode
= StereoEncoding::Hrtf
;
1187 else if(al::case_compare(*hrtfopt
, "false"sv
) == 0)
1189 if(!stereomode
|| *stereomode
== StereoEncoding::Hrtf
)
1190 stereomode
= StereoEncoding::Default
;
1192 else if(al::case_compare(*hrtfopt
, "auto"sv
) != 0)
1193 ERR("Unexpected hrtf value: %s\n", hrtfopt
->c_str());
1197 if(auto encopt
= device
->configValue
<std::string
>({}, "stereo-encoding"sv
))
1199 if(al::case_compare(*encopt
, "basic"sv
) == 0 || al::case_compare(*encopt
, "panpot"sv
) == 0)
1200 stereomode
= StereoEncoding::Basic
;
1201 else if(al::case_compare(*encopt
, "uhj") == 0)
1202 stereomode
= StereoEncoding::Uhj
;
1203 else if(al::case_compare(*encopt
, "hrtf") == 0)
1204 stereomode
= StereoEncoding::Hrtf
;
1206 ERR("Unexpected stereo-encoding: %s\n", encopt
->c_str());
1209 // Check for app-specified attributes
1210 if(!attrList
.empty())
1212 ALenum outmode
{ALC_ANY_SOFT
};
1213 std::optional
<bool> opthrtf
;
1216 #define ATTRIBUTE(a) a: TRACE("%s = %d\n", #a, attrList[attrIdx + 1]);
1217 for(size_t attrIdx
{0};attrIdx
< attrList
.size();attrIdx
+=2)
1219 switch(attrList
[attrIdx
])
1221 case ATTRIBUTE(ALC_FORMAT_CHANNELS_SOFT
)
1222 if(device
->Type
== DeviceType::Loopback
)
1223 optchans
= DevFmtChannelsFromEnum(attrList
[attrIdx
+ 1]);
1226 case ATTRIBUTE(ALC_FORMAT_TYPE_SOFT
)
1227 if(device
->Type
== DeviceType::Loopback
)
1228 opttype
= DevFmtTypeFromEnum(attrList
[attrIdx
+ 1]);
1231 case ATTRIBUTE(ALC_FREQUENCY
)
1232 freqAttr
= attrList
[attrIdx
+ 1];
1235 case ATTRIBUTE(ALC_AMBISONIC_LAYOUT_SOFT
)
1236 if(device
->Type
== DeviceType::Loopback
)
1237 optlayout
= DevAmbiLayoutFromEnum(attrList
[attrIdx
+ 1]);
1240 case ATTRIBUTE(ALC_AMBISONIC_SCALING_SOFT
)
1241 if(device
->Type
== DeviceType::Loopback
)
1242 optscale
= DevAmbiScalingFromEnum(attrList
[attrIdx
+ 1]);
1245 case ATTRIBUTE(ALC_AMBISONIC_ORDER_SOFT
)
1246 if(device
->Type
== DeviceType::Loopback
)
1247 aorder
= static_cast<uint
>(attrList
[attrIdx
+ 1]);
1250 case ATTRIBUTE(ALC_MONO_SOURCES
)
1251 numMono
= static_cast<uint
>(attrList
[attrIdx
+ 1]);
1252 if(numMono
> INT_MAX
) numMono
= 0;
1255 case ATTRIBUTE(ALC_STEREO_SOURCES
)
1256 numStereo
= static_cast<uint
>(attrList
[attrIdx
+ 1]);
1257 if(numStereo
> INT_MAX
) numStereo
= 0;
1260 case ATTRIBUTE(ALC_MAX_AUXILIARY_SENDS
)
1261 numSends
= static_cast<uint
>(attrList
[attrIdx
+ 1]);
1262 if(numSends
> uint
{std::numeric_limits
<int>::max()}) numSends
= 0;
1263 else numSends
= std::min(numSends
, uint
{MaxSendCount
});
1266 case ATTRIBUTE(ALC_HRTF_SOFT
)
1267 if(attrList
[attrIdx
+ 1] == ALC_FALSE
)
1269 else if(attrList
[attrIdx
+ 1] == ALC_TRUE
)
1271 else if(attrList
[attrIdx
+ 1] == ALC_DONT_CARE_SOFT
)
1272 opthrtf
= std::nullopt
;
1275 case ATTRIBUTE(ALC_HRTF_ID_SOFT
)
1276 hrtf_id
= attrList
[attrIdx
+ 1];
1279 case ATTRIBUTE(ALC_OUTPUT_LIMITER_SOFT
)
1280 if(attrList
[attrIdx
+ 1] == ALC_FALSE
)
1282 else if(attrList
[attrIdx
+ 1] == ALC_TRUE
)
1284 else if(attrList
[attrIdx
+ 1] == ALC_DONT_CARE_SOFT
)
1285 optlimit
= std::nullopt
;
1288 case ATTRIBUTE(ALC_OUTPUT_MODE_SOFT
)
1289 outmode
= attrList
[attrIdx
+ 1];
1293 TRACE("0x%04X = %d (0x%x)\n", attrList
[attrIdx
],
1294 attrList
[attrIdx
+ 1], attrList
[attrIdx
+ 1]);
1300 if(device
->Type
== DeviceType::Loopback
)
1302 if(!optchans
|| !opttype
)
1303 return ALC_INVALID_VALUE
;
1304 if(freqAttr
< int{MinOutputRate
} || freqAttr
> int{MaxOutputRate
})
1305 return ALC_INVALID_VALUE
;
1306 if(*optchans
== DevFmtAmbi3D
)
1308 if(!optlayout
|| !optscale
)
1309 return ALC_INVALID_VALUE
;
1310 if(aorder
< 1 || aorder
> MaxAmbiOrder
)
1311 return ALC_INVALID_VALUE
;
1312 if((*optlayout
== DevAmbiLayout::FuMa
|| *optscale
== DevAmbiScaling::FuMa
)
1314 return ALC_INVALID_VALUE
;
1316 else if(*optchans
== DevFmtStereo
)
1321 stereomode
= StereoEncoding::Hrtf
;
1324 if(stereomode
.value_or(StereoEncoding::Hrtf
) == StereoEncoding::Hrtf
)
1325 stereomode
= StereoEncoding::Default
;
1329 if(outmode
== ALC_STEREO_BASIC_SOFT
)
1330 stereomode
= StereoEncoding::Basic
;
1331 else if(outmode
== ALC_STEREO_UHJ_SOFT
)
1332 stereomode
= StereoEncoding::Uhj
;
1333 else if(outmode
== ALC_STEREO_HRTF_SOFT
)
1334 stereomode
= StereoEncoding::Hrtf
;
1337 optsrate
= static_cast<uint
>(freqAttr
);
1344 stereomode
= StereoEncoding::Hrtf
;
1347 if(stereomode
.value_or(StereoEncoding::Hrtf
) == StereoEncoding::Hrtf
)
1348 stereomode
= StereoEncoding::Default
;
1352 if(outmode
!= ALC_ANY_SOFT
)
1354 using OutputMode
= ALCdevice::OutputMode
;
1355 switch(OutputMode(outmode
))
1357 case OutputMode::Any
: break;
1358 case OutputMode::Mono
: optchans
= DevFmtMono
; break;
1359 case OutputMode::Stereo
: optchans
= DevFmtStereo
; break;
1360 case OutputMode::StereoBasic
:
1361 optchans
= DevFmtStereo
;
1362 stereomode
= StereoEncoding::Basic
;
1364 case OutputMode::Uhj2
:
1365 optchans
= DevFmtStereo
;
1366 stereomode
= StereoEncoding::Uhj
;
1368 case OutputMode::Hrtf
:
1369 optchans
= DevFmtStereo
;
1370 stereomode
= StereoEncoding::Hrtf
;
1372 case OutputMode::Quad
: optchans
= DevFmtQuad
; break;
1373 case OutputMode::X51
: optchans
= DevFmtX51
; break;
1374 case OutputMode::X61
: optchans
= DevFmtX61
; break;
1375 case OutputMode::X71
: optchans
= DevFmtX71
; break;
1381 uint oldrate
= optsrate
.value_or(DefaultOutputRate
);
1382 freqAttr
= std::clamp
<int>(freqAttr
, MinOutputRate
, MaxOutputRate
);
1384 const double scale
{static_cast<double>(freqAttr
) / oldrate
};
1385 period_size
= static_cast<uint
>(std::lround(period_size
* scale
));
1386 buffer_size
= static_cast<uint
>(std::lround(buffer_size
* scale
));
1387 optsrate
= static_cast<uint
>(freqAttr
);
1391 /* If a context is already running on the device, stop playback so the
1392 * device attributes can be updated.
1394 if(device
->mDeviceState
== DeviceState::Playing
)
1396 device
->Backend
->stop();
1397 device
->mDeviceState
= DeviceState::Unprepared
;
1400 UpdateClockBase(device
);
1403 if(device
->mDeviceState
== DeviceState::Playing
)
1404 return ALC_NO_ERROR
;
1406 device
->mDeviceState
= DeviceState::Unprepared
;
1407 device
->AvgSpeakerDist
= 0.0f
;
1408 device
->mNFCtrlFilter
= NfcFilter
{};
1409 device
->mUhjEncoder
= nullptr;
1410 device
->AmbiDecoder
= nullptr;
1411 device
->Bs2b
= nullptr;
1412 device
->PostProcess
= nullptr;
1414 device
->Limiter
= nullptr;
1415 device
->ChannelDelays
= nullptr;
1417 std::fill(std::begin(device
->HrtfAccumData
), std::end(device
->HrtfAccumData
), float2
{});
1419 device
->Dry
.AmbiMap
.fill(BFChannelConfig
{});
1420 device
->Dry
.Buffer
= {};
1421 std::fill(std::begin(device
->NumChannelsPerOrder
), std::end(device
->NumChannelsPerOrder
), 0u);
1422 device
->RealOut
.RemixMap
= {};
1423 device
->RealOut
.ChannelIndex
.fill(InvalidChannelIndex
);
1424 device
->RealOut
.Buffer
= {};
1425 device
->MixBuffer
.clear();
1426 device
->MixBuffer
.shrink_to_fit();
1428 UpdateClockBase(device
);
1429 device
->FixedLatency
= nanoseconds::zero();
1431 device
->DitherDepth
= 0.0f
;
1432 device
->DitherSeed
= DitherRNGSeed
;
1434 device
->mHrtfStatus
= ALC_HRTF_DISABLED_SOFT
;
1436 /*************************************************************************
1437 * Update device format request
1440 if(device
->Type
== DeviceType::Loopback
)
1442 device
->Frequency
= *optsrate
;
1443 device
->FmtChans
= *optchans
;
1444 device
->FmtType
= *opttype
;
1445 if(device
->FmtChans
== DevFmtAmbi3D
)
1447 device
->mAmbiOrder
= aorder
;
1448 device
->mAmbiLayout
= *optlayout
;
1449 device
->mAmbiScale
= *optscale
;
1451 device
->Flags
.set(FrequencyRequest
).set(ChannelsRequest
).set(SampleTypeRequest
);
1455 device
->FmtType
= opttype
.value_or(DevFmtTypeDefault
);
1456 device
->FmtChans
= optchans
.value_or(DevFmtChannelsDefault
);
1457 device
->mAmbiOrder
= 0;
1458 device
->BufferSize
= buffer_size
;
1459 device
->UpdateSize
= period_size
;
1460 device
->Frequency
= optsrate
.value_or(DefaultOutputRate
);
1461 device
->Flags
.set(FrequencyRequest
, optsrate
.has_value())
1462 .set(ChannelsRequest
, optchans
.has_value())
1463 .set(SampleTypeRequest
, opttype
.has_value());
1465 if(device
->FmtChans
== DevFmtAmbi3D
)
1467 device
->mAmbiOrder
= std::clamp(aorder
, 1u, uint
{MaxAmbiOrder
});
1468 device
->mAmbiLayout
= optlayout
.value_or(DevAmbiLayout::Default
);
1469 device
->mAmbiScale
= optscale
.value_or(DevAmbiScaling::Default
);
1470 if(device
->mAmbiOrder
> 3
1471 && (device
->mAmbiLayout
== DevAmbiLayout::FuMa
1472 || device
->mAmbiScale
== DevAmbiScaling::FuMa
))
1474 ERR("FuMa is incompatible with %d%s order ambisonics (up to 3rd order only)\n",
1475 device
->mAmbiOrder
, GetCounterSuffix(device
->mAmbiOrder
));
1476 device
->mAmbiOrder
= 3;
1481 TRACE("Pre-reset: %s%s, %s%s, %s%uhz, %u / %u buffer\n",
1482 device
->Flags
.test(ChannelsRequest
)?"*":"", DevFmtChannelsString(device
->FmtChans
),
1483 device
->Flags
.test(SampleTypeRequest
)?"*":"", DevFmtTypeString(device
->FmtType
),
1484 device
->Flags
.test(FrequencyRequest
)?"*":"", device
->Frequency
,
1485 device
->UpdateSize
, device
->BufferSize
);
1487 const uint oldFreq
{device
->Frequency
};
1488 const DevFmtChannels oldChans
{device
->FmtChans
};
1489 const DevFmtType oldType
{device
->FmtType
};
1491 auto backend
= device
->Backend
.get();
1492 if(!backend
->reset())
1493 throw al::backend_exception
{al::backend_error::DeviceError
, "Device reset failure"};
1495 catch(std::exception
&e
) {
1496 ERR("Device error: %s\n", e
.what());
1497 device
->handleDisconnect("%s", e
.what());
1498 return ALC_INVALID_DEVICE
;
1501 if(device
->FmtChans
!= oldChans
&& device
->Flags
.test(ChannelsRequest
))
1503 ERR("Failed to set %s, got %s instead\n", DevFmtChannelsString(oldChans
),
1504 DevFmtChannelsString(device
->FmtChans
));
1505 device
->Flags
.reset(ChannelsRequest
);
1507 if(device
->FmtType
!= oldType
&& device
->Flags
.test(SampleTypeRequest
))
1509 ERR("Failed to set %s, got %s instead\n", DevFmtTypeString(oldType
),
1510 DevFmtTypeString(device
->FmtType
));
1511 device
->Flags
.reset(SampleTypeRequest
);
1513 if(device
->Frequency
!= oldFreq
&& device
->Flags
.test(FrequencyRequest
))
1515 WARN("Failed to set %uhz, got %uhz instead\n", oldFreq
, device
->Frequency
);
1516 device
->Flags
.reset(FrequencyRequest
);
1519 TRACE("Post-reset: %s, %s, %uhz, %u / %u buffer\n",
1520 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1521 device
->Frequency
, device
->UpdateSize
, device
->BufferSize
);
1523 if(device
->Type
!= DeviceType::Loopback
)
1525 if(auto modeopt
= device
->configValue
<std::string
>({}, "stereo-mode"))
1527 if(al::case_compare(*modeopt
, "headphones"sv
) == 0)
1528 device
->Flags
.set(DirectEar
);
1529 else if(al::case_compare(*modeopt
, "speakers"sv
) == 0)
1530 device
->Flags
.reset(DirectEar
);
1531 else if(al::case_compare(*modeopt
, "auto"sv
) != 0)
1532 ERR("Unexpected stereo-mode: %s\n", modeopt
->c_str());
1536 aluInitRenderer(device
, hrtf_id
, stereomode
);
1538 /* Calculate the max number of sources, and split them between the mono and
1539 * stereo count given the requested number of stereo sources.
1541 if(auto srcsopt
= device
->configValue
<uint
>({}, "sources"sv
))
1543 if(*srcsopt
<= 0) numMono
= 256;
1544 else numMono
= std::max(*srcsopt
, 16u);
1548 numMono
= std::min(numMono
, std::numeric_limits
<int>::max()-numStereo
);
1549 numMono
= std::max(numMono
+numStereo
, 256u);
1551 numStereo
= std::min(numStereo
, numMono
);
1552 numMono
-= numStereo
;
1553 device
->SourcesMax
= numMono
+ numStereo
;
1554 device
->NumMonoSources
= numMono
;
1555 device
->NumStereoSources
= numStereo
;
1557 if(auto sendsopt
= device
->configValue
<uint
>({}, "sends"sv
))
1558 numSends
= std::min(numSends
, std::clamp(*sendsopt
, 0u, uint
{MaxSendCount
}));
1559 device
->NumAuxSends
= numSends
;
1561 TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n",
1562 device
->SourcesMax
, device
->NumMonoSources
, device
->NumStereoSources
,
1563 device
->AuxiliaryEffectSlotMax
, device
->NumAuxSends
);
1565 switch(device
->FmtChans
)
1567 case DevFmtMono
: break;
1569 if(!device
->mUhjEncoder
)
1570 device
->RealOut
.RemixMap
= StereoDownmix
;
1572 case DevFmtQuad
: device
->RealOut
.RemixMap
= QuadDownmix
; break;
1573 case DevFmtX51
: device
->RealOut
.RemixMap
= X51Downmix
; break;
1574 case DevFmtX61
: device
->RealOut
.RemixMap
= X61Downmix
; break;
1575 case DevFmtX71
: device
->RealOut
.RemixMap
= X71Downmix
; break;
1576 case DevFmtX714
: device
->RealOut
.RemixMap
= X71Downmix
; break;
1577 case DevFmtX7144
: device
->RealOut
.RemixMap
= X71Downmix
; break;
1578 case DevFmtX3D71
: device
->RealOut
.RemixMap
= X51Downmix
; break;
1579 case DevFmtAmbi3D
: break;
1582 size_t sample_delay
{0};
1583 if(auto *encoder
{device
->mUhjEncoder
.get()})
1584 sample_delay
+= encoder
->getDelay();
1586 if(device
->getConfigValueBool({}, "dither"sv
, true))
1588 int depth
{device
->configValue
<int>({}, "dither-depth"sv
).value_or(0)};
1591 switch(device
->FmtType
)
1610 depth
= std::clamp(depth
, 2, 24);
1611 device
->DitherDepth
= std::pow(2.0f
, static_cast<float>(depth
-1));
1614 if(!(device
->DitherDepth
> 0.0f
))
1615 TRACE("Dithering disabled\n");
1617 TRACE("Dithering enabled (%d-bit, %g)\n", float2int(std::log2(device
->DitherDepth
)+0.5f
)+1,
1618 device
->DitherDepth
);
1621 optlimit
= device
->configValue
<bool>({}, "output-limiter");
1623 /* If the gain limiter is unset, use the limiter for integer-based output
1624 * (where samples must be clamped), and don't for floating-point (which can
1625 * take unclamped samples).
1629 switch(device
->FmtType
)
1643 if(!optlimit
.value_or(false))
1644 TRACE("Output limiter disabled\n");
1647 float thrshld
{1.0f
};
1648 switch(device
->FmtType
)
1652 thrshld
= 127.0f
/ 128.0f
;
1656 thrshld
= 32767.0f
/ 32768.0f
;
1663 if(device
->DitherDepth
> 0.0f
)
1664 thrshld
-= 1.0f
/ device
->DitherDepth
;
1666 const float thrshld_dB
{std::log10(thrshld
) * 20.0f
};
1667 auto limiter
= CreateDeviceLimiter(device
, thrshld_dB
);
1669 sample_delay
+= limiter
->getLookAhead();
1670 device
->Limiter
= std::move(limiter
);
1671 TRACE("Output limiter enabled, %.4fdB limit\n", thrshld_dB
);
1674 /* Convert the sample delay from samples to nanosamples to nanoseconds. */
1675 sample_delay
= std::min
<size_t>(sample_delay
, std::numeric_limits
<int>::max());
1676 device
->FixedLatency
+= nanoseconds
{seconds
{sample_delay
}} / device
->Frequency
;
1677 TRACE("Fixed device latency: %" PRId64
"ns\n", int64_t{device
->FixedLatency
.count()});
1679 FPUCtl mixer_mode
{};
1680 auto reset_context
= [device
](ContextBase
*ctxbase
)
1682 auto *context
= dynamic_cast<ALCcontext
*>(ctxbase
);
1683 assert(context
!= nullptr);
1684 if(!context
) return;
1686 std::unique_lock
<std::mutex
> proplock
{context
->mPropLock
};
1687 std::unique_lock
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
1689 /* Clear out unused effect slot clusters. */
1690 auto slot_cluster_not_in_use
= [](ContextBase::EffectSlotCluster
&clusterptr
) -> bool
1692 return std::none_of(clusterptr
->begin(), clusterptr
->end(),
1693 std::mem_fn(&EffectSlot::InUse
));
1695 auto slotcluster_end
= std::remove_if(context
->mEffectSlotClusters
.begin(),
1696 context
->mEffectSlotClusters
.end(), slot_cluster_not_in_use
);
1697 context
->mEffectSlotClusters
.erase(slotcluster_end
, context
->mEffectSlotClusters
.end());
1699 /* Free all wet buffers. Any in use will be reallocated with an updated
1700 * configuration in aluInitEffectPanning.
1702 auto clear_wetbuffers
= [](ContextBase::EffectSlotCluster
&clusterptr
)
1704 auto clear_buffer
= [](EffectSlot
&slot
)
1706 slot
.mWetBuffer
.clear();
1707 slot
.mWetBuffer
.shrink_to_fit();
1708 slot
.Wet
.Buffer
= {};
1710 std::for_each(clusterptr
->begin(), clusterptr
->end(), clear_buffer
);
1712 std::for_each(context
->mEffectSlotClusters
.begin(), context
->mEffectSlotClusters
.end(),
1715 if(ALeffectslot
*slot
{context
->mDefaultSlot
.get()})
1717 auto *slotbase
= slot
->mSlot
;
1718 aluInitEffectPanning(slotbase
, context
);
1720 if(auto *props
= slotbase
->Update
.exchange(nullptr, std::memory_order_relaxed
))
1721 AtomicReplaceHead(context
->mFreeEffectSlotProps
, props
);
1723 EffectState
*state
{slot
->Effect
.State
.get()};
1724 state
->mOutTarget
= device
->Dry
.Buffer
;
1725 state
->deviceUpdate(device
, slot
->Buffer
);
1726 slot
->mPropsDirty
= true;
1729 if(EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_relaxed
)})
1730 std::fill(curarray
->begin()+ptrdiff_t(curarray
->size()>>1), curarray
->end(), nullptr);
1731 auto reset_slots
= [device
,context
](EffectSlotSubList
&sublist
)
1733 uint64_t usemask
{~sublist
.FreeMask
};
1736 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
1737 auto &slot
= (*sublist
.EffectSlots
)[idx
];
1738 usemask
&= ~(1_u64
<< idx
);
1740 auto *slotbase
= slot
.mSlot
;
1741 aluInitEffectPanning(slotbase
, context
);
1743 if(auto *props
= slotbase
->Update
.exchange(nullptr, std::memory_order_relaxed
))
1744 AtomicReplaceHead(context
->mFreeEffectSlotProps
, props
);
1746 EffectState
*state
{slot
.Effect
.State
.get()};
1747 state
->mOutTarget
= device
->Dry
.Buffer
;
1748 state
->deviceUpdate(device
, slot
.Buffer
);
1749 slot
.mPropsDirty
= true;
1752 std::for_each(context
->mEffectSlotList
.begin(), context
->mEffectSlotList
.end(),
1755 /* Clear all effect slot props to let them get allocated again. */
1756 context
->mEffectSlotPropClusters
.clear();
1757 context
->mFreeEffectSlotProps
.store(nullptr, std::memory_order_relaxed
);
1760 std::unique_lock
<std::mutex
> srclock
{context
->mSourceLock
};
1761 const uint num_sends
{device
->NumAuxSends
};
1762 auto reset_sources
= [num_sends
](SourceSubList
&sublist
)
1764 uint64_t usemask
{~sublist
.FreeMask
};
1767 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
1768 auto &source
= (*sublist
.Sources
)[idx
];
1769 usemask
&= ~(1_u64
<< idx
);
1771 auto clear_send
= [](ALsource::SendData
&send
) -> void
1774 DecrementRef(send
.Slot
->ref
);
1775 send
.Slot
= nullptr;
1778 send
.HFReference
= LowPassFreqRef
;
1780 send
.LFReference
= HighPassFreqRef
;
1782 const auto sends
= al::span
{source
.Send
}.subspan(num_sends
);
1783 std::for_each(sends
.begin(), sends
.end(), clear_send
);
1785 source
.mPropsDirty
= true;
1788 std::for_each(context
->mSourceList
.begin(), context
->mSourceList
.end(), reset_sources
);
1790 auto reset_voice
= [device
,num_sends
,context
](Voice
*voice
)
1792 /* Clear extraneous property set sends. */
1793 const auto sendparams
= al::span
{voice
->mProps
.Send
}.subspan(num_sends
);
1794 std::fill(sendparams
.begin(), sendparams
.end(), VoiceProps::SendData
{});
1796 std::fill(voice
->mSend
.begin()+num_sends
, voice
->mSend
.end(), Voice::TargetData
{});
1797 auto clear_wetparams
= [num_sends
](Voice::ChannelData
&chandata
)
1799 const auto wetparams
= al::span
{chandata
.mWetParams
}.subspan(num_sends
);
1800 std::fill(wetparams
.begin(), wetparams
.end(), SendParams
{});
1802 std::for_each(voice
->mChans
.begin(), voice
->mChans
.end(), clear_wetparams
);
1804 if(VoicePropsItem
*props
{voice
->mUpdate
.exchange(nullptr, std::memory_order_relaxed
)})
1805 AtomicReplaceHead(context
->mFreeVoiceProps
, props
);
1807 /* Force the voice to stopped if it was stopping. */
1808 Voice::State vstate
{Voice::Stopping
};
1809 voice
->mPlayState
.compare_exchange_strong(vstate
, Voice::Stopped
,
1810 std::memory_order_acquire
, std::memory_order_acquire
);
1811 if(voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u)
1814 voice
->prepare(device
);
1816 const auto voicespan
= context
->getVoicesSpan();
1817 std::for_each(voicespan
.begin(), voicespan
.end(), reset_voice
);
1819 /* Clear all voice props to let them get allocated again. */
1820 context
->mVoicePropClusters
.clear();
1821 context
->mFreeVoiceProps
.store(nullptr, std::memory_order_relaxed
);
1824 context
->mPropsDirty
= false;
1825 UpdateContextProps(context
);
1826 UpdateAllEffectSlotProps(context
);
1827 UpdateAllSourceProps(context
);
1829 auto ctxspan
= al::span
{*device
->mContexts
.load()};
1830 std::for_each(ctxspan
.begin(), ctxspan
.end(), reset_context
);
1833 device
->mDeviceState
= DeviceState::Configured
;
1834 if(!device
->Flags
.test(DevicePaused
))
1837 auto backend
= device
->Backend
.get();
1839 device
->mDeviceState
= DeviceState::Playing
;
1841 catch(al::backend_exception
& e
) {
1842 ERR("%s\n", e
.what());
1843 device
->handleDisconnect("%s", e
.what());
1844 return ALC_INVALID_DEVICE
;
1846 TRACE("Post-start: %s, %s, %uhz, %u / %u buffer\n",
1847 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1848 device
->Frequency
, device
->UpdateSize
, device
->BufferSize
);
1851 return ALC_NO_ERROR
;
1855 * Updates device parameters as above, and also first clears the disconnected
1858 bool ResetDeviceParams(ALCdevice
*device
, const al::span
<const int> attrList
)
1860 /* If the device was disconnected, reset it since we're opened anew. */
1861 if(!device
->Connected
.load(std::memory_order_relaxed
)) UNLIKELY
1863 /* Make sure disconnection is finished before continuing on. */
1864 std::ignore
= device
->waitForMix();
1866 for(ContextBase
*ctxbase
: *device
->mContexts
.load(std::memory_order_acquire
))
1868 auto *ctx
= dynamic_cast<ALCcontext
*>(ctxbase
);
1869 assert(ctx
!= nullptr);
1870 if(!ctx
|| !ctx
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
1873 /* Clear any pending voice changes and reallocate voices to get a
1876 std::lock_guard
<std::mutex
> sourcelock
{ctx
->mSourceLock
};
1877 auto *vchg
= ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
);
1878 while(auto *next
= vchg
->mNext
.load(std::memory_order_acquire
))
1880 ctx
->mCurrentVoiceChange
.store(vchg
, std::memory_order_release
);
1882 ctx
->mVoicePropClusters
.clear();
1883 ctx
->mFreeVoiceProps
.store(nullptr, std::memory_order_relaxed
);
1885 ctx
->mVoiceClusters
.clear();
1886 ctx
->allocVoices(std::max
<size_t>(256,
1887 ctx
->mActiveVoiceCount
.load(std::memory_order_relaxed
)));
1890 device
->Connected
.store(true);
1893 ALCenum err
{UpdateDeviceParams(device
, attrList
)};
1894 if(err
== ALC_NO_ERROR
) LIKELY
return ALC_TRUE
;
1896 alcSetError(device
, err
);
1901 /** Checks if the device handle is valid, and returns a new reference if so. */
1902 DeviceRef
VerifyDevice(ALCdevice
*device
)
1904 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
1905 auto iter
= std::lower_bound(DeviceList
.begin(), DeviceList
.end(), device
);
1906 if(iter
!= DeviceList
.end() && *iter
== device
)
1909 return DeviceRef
{*iter
};
1916 * Checks if the given context is valid, returning a new reference to it if so.
1918 ContextRef
VerifyContext(ALCcontext
*context
)
1920 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
1921 auto iter
= std::lower_bound(ContextList
.begin(), ContextList
.end(), context
);
1922 if(iter
!= ContextList
.end() && *iter
== context
)
1925 return ContextRef
{*iter
};
1932 FORCE_ALIGN
void ALC_APIENTRY
alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback
, void *userptr
) noexcept
1934 al_set_log_callback(callback
, userptr
);
1937 /** Returns a new reference to the currently active context for this thread. */
1938 ContextRef
GetContextRef() noexcept
1940 ALCcontext
*context
{ALCcontext::getThreadContext()};
1945 while(ALCcontext::sGlobalContextLock
.exchange(true, std::memory_order_acquire
)) {
1946 /* Wait to make sure another thread isn't trying to change the
1947 * current context and bring its refcount to 0.
1950 context
= ALCcontext::sGlobalContext
.load(std::memory_order_acquire
);
1951 if(context
) LIKELY context
->add_ref();
1952 ALCcontext::sGlobalContextLock
.store(false, std::memory_order_release
);
1954 return ContextRef
{context
};
1957 void alcSetError(ALCdevice
*device
, ALCenum errorCode
)
1959 WARN("Error generated on device %p, code 0x%04x\n", voidp
{device
}, errorCode
);
1963 /* DebugBreak() will cause an exception if there is no debugger */
1964 if(IsDebuggerPresent())
1966 #elif defined(SIGTRAP)
1972 device
->LastError
.store(errorCode
);
1974 LastNullDeviceError
.store(errorCode
);
1977 /************************************************
1978 * Standard ALC functions
1979 ************************************************/
1981 ALC_API ALCenum ALC_APIENTRY
alcGetError(ALCdevice
*device
) noexcept
1983 if(!gProcessRunning
)
1984 return ALC_INVALID_DEVICE
;
1986 DeviceRef dev
{VerifyDevice(device
)};
1987 if(dev
) return dev
->LastError
.exchange(ALC_NO_ERROR
);
1988 return LastNullDeviceError
.exchange(ALC_NO_ERROR
);
1992 ALC_API
void ALC_APIENTRY
alcSuspendContext(ALCcontext
*context
) noexcept
1994 ContextRef ctx
{VerifyContext(context
)};
1997 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2001 if(ctx
->mContextFlags
.test(ContextFlags::DebugBit
)) UNLIKELY
2002 ctx
->debugMessage(DebugSource::API
, DebugType::Portability
, 0, DebugSeverity::Medium
,
2003 "alcSuspendContext behavior is not portable -- some implementations suspend all "
2004 "rendering, some only defer property changes, and some are completely no-op; consider "
2005 "using alcDevicePauseSOFT to suspend all rendering, or alDeferUpdatesSOFT to only "
2006 "defer property changes");
2010 std::lock_guard
<std::mutex
> proplock
{ctx
->mPropLock
};
2011 ctx
->deferUpdates();
2015 ALC_API
void ALC_APIENTRY
alcProcessContext(ALCcontext
*context
) noexcept
2017 ContextRef ctx
{VerifyContext(context
)};
2020 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2024 if(ctx
->mContextFlags
.test(ContextFlags::DebugBit
)) UNLIKELY
2025 ctx
->debugMessage(DebugSource::API
, DebugType::Portability
, 1, DebugSeverity::Medium
,
2026 "alcProcessContext behavior is not portable -- some implementations resume rendering, "
2027 "some apply deferred property changes, and some are completely no-op; consider using "
2028 "alcDeviceResumeSOFT to resume rendering, or alProcessUpdatesSOFT to apply deferred "
2029 "property changes");
2033 std::lock_guard
<std::mutex
> proplock
{ctx
->mPropLock
};
2034 ctx
->processUpdates();
2039 ALC_API
auto ALC_APIENTRY
alcGetString(ALCdevice
*Device
, ALCenum param
) noexcept
-> const ALCchar
*
2043 case ALC_NO_ERROR
: return GetNoErrorString();
2044 case ALC_INVALID_ENUM
: return GetInvalidEnumString();
2045 case ALC_INVALID_VALUE
: return GetInvalidValueString();
2046 case ALC_INVALID_DEVICE
: return GetInvalidDeviceString();
2047 case ALC_INVALID_CONTEXT
: return GetInvalidContextString();
2048 case ALC_OUT_OF_MEMORY
: return GetOutOfMemoryString();
2050 case ALC_DEVICE_SPECIFIER
:
2051 return GetDefaultName();
2053 case ALC_ALL_DEVICES_SPECIFIER
:
2054 if(DeviceRef dev
{VerifyDevice(Device
)})
2056 if(dev
->Type
== DeviceType::Capture
)
2058 alcSetError(dev
.get(), ALC_INVALID_ENUM
);
2061 if(dev
->Type
== DeviceType::Loopback
)
2062 return GetDefaultName();
2064 auto statelock
= std::lock_guard
{dev
->StateLock
};
2065 return dev
->mDeviceName
.c_str();
2067 ProbeAllDevicesList();
2068 return alcAllDevicesList
.c_str();
2070 case ALC_CAPTURE_DEVICE_SPECIFIER
:
2071 if(DeviceRef dev
{VerifyDevice(Device
)})
2073 if(dev
->Type
!= DeviceType::Capture
)
2075 alcSetError(dev
.get(), ALC_INVALID_ENUM
);
2079 auto statelock
= std::lock_guard
{dev
->StateLock
};
2080 return dev
->mDeviceName
.c_str();
2082 ProbeCaptureDeviceList();
2083 return alcCaptureDeviceList
.c_str();
2085 /* Default devices are always first in the list */
2086 case ALC_DEFAULT_DEVICE_SPECIFIER
:
2087 return GetDefaultName();
2089 case ALC_DEFAULT_ALL_DEVICES_SPECIFIER
:
2090 if(alcAllDevicesList
.empty())
2091 ProbeAllDevicesList();
2093 /* Copy first entry as default. */
2094 if(alcAllDevicesArray
.empty())
2095 return GetDefaultName();
2097 alcDefaultAllDevicesSpecifier
= alcAllDevicesArray
.front();
2098 return alcDefaultAllDevicesSpecifier
.c_str();
2100 case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER
:
2101 if(alcCaptureDeviceList
.empty())
2102 ProbeCaptureDeviceList();
2104 /* Copy first entry as default. */
2105 if(alcCaptureDeviceArray
.empty())
2106 return GetDefaultName();
2108 alcCaptureDefaultDeviceSpecifier
= alcCaptureDeviceArray
.front();
2109 return alcCaptureDefaultDeviceSpecifier
.c_str();
2111 case ALC_EXTENSIONS
:
2112 if(VerifyDevice(Device
))
2113 return GetExtensionList();
2114 return GetNoDeviceExtList();
2116 case ALC_HRTF_SPECIFIER_SOFT
:
2117 if(DeviceRef dev
{VerifyDevice(Device
)})
2119 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
2120 return dev
->mHrtf
? dev
->mHrtfName
.c_str() : "";
2122 alcSetError(nullptr, ALC_INVALID_DEVICE
);
2126 alcSetError(VerifyDevice(Device
).get(), ALC_INVALID_ENUM
);
2133 static size_t GetIntegerv(ALCdevice
*device
, ALCenum param
, const al::span
<int> values
)
2137 alcSetError(device
, ALC_INVALID_VALUE
);
2145 case ALC_MAJOR_VERSION
:
2146 values
[0] = alcMajorVersion
;
2148 case ALC_MINOR_VERSION
:
2149 values
[0] = alcMinorVersion
;
2152 case ALC_EFX_MAJOR_VERSION
:
2153 values
[0] = alcEFXMajorVersion
;
2155 case ALC_EFX_MINOR_VERSION
:
2156 values
[0] = alcEFXMinorVersion
;
2158 case ALC_MAX_AUXILIARY_SENDS
:
2159 values
[0] = MaxSendCount
;
2162 case ALC_ATTRIBUTES_SIZE
:
2163 case ALC_ALL_ATTRIBUTES
:
2167 case ALC_MONO_SOURCES
:
2168 case ALC_STEREO_SOURCES
:
2169 case ALC_CAPTURE_SAMPLES
:
2170 case ALC_FORMAT_CHANNELS_SOFT
:
2171 case ALC_FORMAT_TYPE_SOFT
:
2172 case ALC_AMBISONIC_LAYOUT_SOFT
:
2173 case ALC_AMBISONIC_SCALING_SOFT
:
2174 case ALC_AMBISONIC_ORDER_SOFT
:
2175 case ALC_MAX_AMBISONIC_ORDER_SOFT
:
2176 alcSetError(nullptr, ALC_INVALID_DEVICE
);
2180 alcSetError(nullptr, ALC_INVALID_ENUM
);
2185 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2186 if(device
->Type
== DeviceType::Capture
)
2188 static constexpr int MaxCaptureAttributes
{9};
2191 case ALC_ATTRIBUTES_SIZE
:
2192 values
[0] = MaxCaptureAttributes
;
2194 case ALC_ALL_ATTRIBUTES
:
2195 if(values
.size() >= MaxCaptureAttributes
)
2198 values
[i
++] = ALC_MAJOR_VERSION
;
2199 values
[i
++] = alcMajorVersion
;
2200 values
[i
++] = ALC_MINOR_VERSION
;
2201 values
[i
++] = alcMinorVersion
;
2202 values
[i
++] = ALC_CAPTURE_SAMPLES
;
2203 values
[i
++] = static_cast<int>(device
->Backend
->availableSamples());
2204 values
[i
++] = ALC_CONNECTED
;
2205 values
[i
++] = device
->Connected
.load(std::memory_order_relaxed
);
2207 assert(i
== MaxCaptureAttributes
);
2210 alcSetError(device
, ALC_INVALID_VALUE
);
2213 case ALC_MAJOR_VERSION
:
2214 values
[0] = alcMajorVersion
;
2216 case ALC_MINOR_VERSION
:
2217 values
[0] = alcMinorVersion
;
2220 case ALC_CAPTURE_SAMPLES
:
2221 values
[0] = static_cast<int>(device
->Backend
->availableSamples());
2225 values
[0] = device
->Connected
.load(std::memory_order_acquire
);
2229 alcSetError(device
, ALC_INVALID_ENUM
);
2235 auto NumAttrsForDevice
= [](const ALCdevice
*aldev
) noexcept
-> uint8_t
2237 if(aldev
->Type
== DeviceType::Loopback
&& aldev
->FmtChans
== DevFmtAmbi3D
)
2243 case ALC_ATTRIBUTES_SIZE
:
2244 values
[0] = NumAttrsForDevice(device
);
2247 case ALC_ALL_ATTRIBUTES
:
2248 if(values
.size() >= NumAttrsForDevice(device
))
2251 values
[i
++] = ALC_MAJOR_VERSION
;
2252 values
[i
++] = alcMajorVersion
;
2253 values
[i
++] = ALC_MINOR_VERSION
;
2254 values
[i
++] = alcMinorVersion
;
2255 values
[i
++] = ALC_EFX_MAJOR_VERSION
;
2256 values
[i
++] = alcEFXMajorVersion
;
2257 values
[i
++] = ALC_EFX_MINOR_VERSION
;
2258 values
[i
++] = alcEFXMinorVersion
;
2260 values
[i
++] = ALC_FREQUENCY
;
2261 values
[i
++] = static_cast<int>(device
->Frequency
);
2262 if(device
->Type
!= DeviceType::Loopback
)
2264 values
[i
++] = ALC_REFRESH
;
2265 values
[i
++] = static_cast<int>(device
->Frequency
/ device
->UpdateSize
);
2267 values
[i
++] = ALC_SYNC
;
2268 values
[i
++] = ALC_FALSE
;
2272 if(device
->FmtChans
== DevFmtAmbi3D
)
2274 values
[i
++] = ALC_AMBISONIC_LAYOUT_SOFT
;
2275 values
[i
++] = EnumFromDevAmbi(device
->mAmbiLayout
);
2277 values
[i
++] = ALC_AMBISONIC_SCALING_SOFT
;
2278 values
[i
++] = EnumFromDevAmbi(device
->mAmbiScale
);
2280 values
[i
++] = ALC_AMBISONIC_ORDER_SOFT
;
2281 values
[i
++] = static_cast<int>(device
->mAmbiOrder
);
2284 values
[i
++] = ALC_FORMAT_CHANNELS_SOFT
;
2285 values
[i
++] = EnumFromDevFmt(device
->FmtChans
);
2287 values
[i
++] = ALC_FORMAT_TYPE_SOFT
;
2288 values
[i
++] = EnumFromDevFmt(device
->FmtType
);
2291 values
[i
++] = ALC_MONO_SOURCES
;
2292 values
[i
++] = static_cast<int>(device
->NumMonoSources
);
2294 values
[i
++] = ALC_STEREO_SOURCES
;
2295 values
[i
++] = static_cast<int>(device
->NumStereoSources
);
2297 values
[i
++] = ALC_MAX_AUXILIARY_SENDS
;
2298 values
[i
++] = static_cast<int>(device
->NumAuxSends
);
2300 values
[i
++] = ALC_HRTF_SOFT
;
2301 values
[i
++] = (device
->mHrtf
? ALC_TRUE
: ALC_FALSE
);
2303 values
[i
++] = ALC_HRTF_STATUS_SOFT
;
2304 values
[i
++] = device
->mHrtfStatus
;
2306 values
[i
++] = ALC_OUTPUT_LIMITER_SOFT
;
2307 values
[i
++] = device
->Limiter
? ALC_TRUE
: ALC_FALSE
;
2309 values
[i
++] = ALC_MAX_AMBISONIC_ORDER_SOFT
;
2310 values
[i
++] = MaxAmbiOrder
;
2312 values
[i
++] = ALC_OUTPUT_MODE_SOFT
;
2313 values
[i
++] = static_cast<ALCenum
>(device
->getOutputMode1());
2316 assert(i
== NumAttrsForDevice(device
));
2319 alcSetError(device
, ALC_INVALID_VALUE
);
2322 case ALC_MAJOR_VERSION
:
2323 values
[0] = alcMajorVersion
;
2326 case ALC_MINOR_VERSION
:
2327 values
[0] = alcMinorVersion
;
2330 case ALC_EFX_MAJOR_VERSION
:
2331 values
[0] = alcEFXMajorVersion
;
2334 case ALC_EFX_MINOR_VERSION
:
2335 values
[0] = alcEFXMinorVersion
;
2339 values
[0] = static_cast<int>(device
->Frequency
);
2343 if(device
->Type
== DeviceType::Loopback
)
2345 alcSetError(device
, ALC_INVALID_DEVICE
);
2348 values
[0] = static_cast<int>(device
->Frequency
/ device
->UpdateSize
);
2352 if(device
->Type
== DeviceType::Loopback
)
2354 alcSetError(device
, ALC_INVALID_DEVICE
);
2357 values
[0] = ALC_FALSE
;
2360 case ALC_FORMAT_CHANNELS_SOFT
:
2361 if(device
->Type
!= DeviceType::Loopback
)
2363 alcSetError(device
, ALC_INVALID_DEVICE
);
2366 values
[0] = EnumFromDevFmt(device
->FmtChans
);
2369 case ALC_FORMAT_TYPE_SOFT
:
2370 if(device
->Type
!= DeviceType::Loopback
)
2372 alcSetError(device
, ALC_INVALID_DEVICE
);
2375 values
[0] = EnumFromDevFmt(device
->FmtType
);
2378 case ALC_AMBISONIC_LAYOUT_SOFT
:
2379 if(device
->Type
!= DeviceType::Loopback
|| device
->FmtChans
!= DevFmtAmbi3D
)
2381 alcSetError(device
, ALC_INVALID_DEVICE
);
2384 values
[0] = EnumFromDevAmbi(device
->mAmbiLayout
);
2387 case ALC_AMBISONIC_SCALING_SOFT
:
2388 if(device
->Type
!= DeviceType::Loopback
|| device
->FmtChans
!= DevFmtAmbi3D
)
2390 alcSetError(device
, ALC_INVALID_DEVICE
);
2393 values
[0] = EnumFromDevAmbi(device
->mAmbiScale
);
2396 case ALC_AMBISONIC_ORDER_SOFT
:
2397 if(device
->Type
!= DeviceType::Loopback
|| device
->FmtChans
!= DevFmtAmbi3D
)
2399 alcSetError(device
, ALC_INVALID_DEVICE
);
2402 values
[0] = static_cast<int>(device
->mAmbiOrder
);
2405 case ALC_MONO_SOURCES
:
2406 values
[0] = static_cast<int>(device
->NumMonoSources
);
2409 case ALC_STEREO_SOURCES
:
2410 values
[0] = static_cast<int>(device
->NumStereoSources
);
2413 case ALC_MAX_AUXILIARY_SENDS
:
2414 values
[0] = static_cast<int>(device
->NumAuxSends
);
2418 values
[0] = device
->Connected
.load(std::memory_order_acquire
);
2422 values
[0] = (device
->mHrtf
? ALC_TRUE
: ALC_FALSE
);
2425 case ALC_HRTF_STATUS_SOFT
:
2426 values
[0] = device
->mHrtfStatus
;
2429 case ALC_NUM_HRTF_SPECIFIERS_SOFT
:
2430 device
->enumerateHrtfs();
2431 values
[0] = static_cast<int>(std::min(device
->mHrtfList
.size(),
2432 size_t{std::numeric_limits
<int>::max()}));
2435 case ALC_OUTPUT_LIMITER_SOFT
:
2436 values
[0] = device
->Limiter
? ALC_TRUE
: ALC_FALSE
;
2439 case ALC_MAX_AMBISONIC_ORDER_SOFT
:
2440 values
[0] = MaxAmbiOrder
;
2443 case ALC_OUTPUT_MODE_SOFT
:
2444 values
[0] = static_cast<ALCenum
>(device
->getOutputMode1());
2448 alcSetError(device
, ALC_INVALID_ENUM
);
2453 ALC_API
void ALC_APIENTRY
alcGetIntegerv(ALCdevice
*device
, ALCenum param
, ALCsizei size
, ALCint
*values
) noexcept
2455 DeviceRef dev
{VerifyDevice(device
)};
2456 if(size
<= 0 || values
== nullptr)
2457 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2459 GetIntegerv(dev
.get(), param
, {values
, static_cast<uint
>(size
)});
2462 ALC_API
void ALC_APIENTRY
alcGetInteger64vSOFT(ALCdevice
*device
, ALCenum pname
, ALCsizei size
, ALCint64SOFT
*values
) noexcept
2464 DeviceRef dev
{VerifyDevice(device
)};
2465 if(size
<= 0 || values
== nullptr)
2467 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2470 const auto valuespan
= al::span
{values
, static_cast<uint
>(size
)};
2471 if(!dev
|| dev
->Type
== DeviceType::Capture
)
2473 auto ivals
= std::vector
<int>(valuespan
.size());
2474 if(size_t got
{GetIntegerv(dev
.get(), pname
, ivals
)})
2475 std::copy_n(ivals
.cbegin(), got
, valuespan
.begin());
2479 auto NumAttrsForDevice
= [](ALCdevice
*aldev
) noexcept
-> size_t
2481 if(aldev
->Type
== DeviceType::Loopback
&& aldev
->FmtChans
== DevFmtAmbi3D
)
2485 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
2488 case ALC_ATTRIBUTES_SIZE
:
2489 valuespan
[0] = static_cast<ALCint64SOFT
>(NumAttrsForDevice(dev
.get()));
2492 case ALC_ALL_ATTRIBUTES
:
2493 if(valuespan
.size() < NumAttrsForDevice(dev
.get()))
2494 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2498 valuespan
[i
++] = ALC_FREQUENCY
;
2499 valuespan
[i
++] = dev
->Frequency
;
2501 if(dev
->Type
!= DeviceType::Loopback
)
2503 valuespan
[i
++] = ALC_REFRESH
;
2504 valuespan
[i
++] = dev
->Frequency
/ dev
->UpdateSize
;
2506 valuespan
[i
++] = ALC_SYNC
;
2507 valuespan
[i
++] = ALC_FALSE
;
2511 valuespan
[i
++] = ALC_FORMAT_CHANNELS_SOFT
;
2512 valuespan
[i
++] = EnumFromDevFmt(dev
->FmtChans
);
2514 valuespan
[i
++] = ALC_FORMAT_TYPE_SOFT
;
2515 valuespan
[i
++] = EnumFromDevFmt(dev
->FmtType
);
2517 if(dev
->FmtChans
== DevFmtAmbi3D
)
2519 valuespan
[i
++] = ALC_AMBISONIC_LAYOUT_SOFT
;
2520 valuespan
[i
++] = EnumFromDevAmbi(dev
->mAmbiLayout
);
2522 valuespan
[i
++] = ALC_AMBISONIC_SCALING_SOFT
;
2523 valuespan
[i
++] = EnumFromDevAmbi(dev
->mAmbiScale
);
2525 valuespan
[i
++] = ALC_AMBISONIC_ORDER_SOFT
;
2526 valuespan
[i
++] = dev
->mAmbiOrder
;
2530 valuespan
[i
++] = ALC_MONO_SOURCES
;
2531 valuespan
[i
++] = dev
->NumMonoSources
;
2533 valuespan
[i
++] = ALC_STEREO_SOURCES
;
2534 valuespan
[i
++] = dev
->NumStereoSources
;
2536 valuespan
[i
++] = ALC_MAX_AUXILIARY_SENDS
;
2537 valuespan
[i
++] = dev
->NumAuxSends
;
2539 valuespan
[i
++] = ALC_HRTF_SOFT
;
2540 valuespan
[i
++] = (dev
->mHrtf
? ALC_TRUE
: ALC_FALSE
);
2542 valuespan
[i
++] = ALC_HRTF_STATUS_SOFT
;
2543 valuespan
[i
++] = dev
->mHrtfStatus
;
2545 valuespan
[i
++] = ALC_OUTPUT_LIMITER_SOFT
;
2546 valuespan
[i
++] = dev
->Limiter
? ALC_TRUE
: ALC_FALSE
;
2548 ClockLatency clock
{GetClockLatency(dev
.get(), dev
->Backend
.get())};
2549 valuespan
[i
++] = ALC_DEVICE_CLOCK_SOFT
;
2550 valuespan
[i
++] = clock
.ClockTime
.count();
2552 valuespan
[i
++] = ALC_DEVICE_LATENCY_SOFT
;
2553 valuespan
[i
++] = clock
.Latency
.count();
2555 valuespan
[i
++] = ALC_OUTPUT_MODE_SOFT
;
2556 valuespan
[i
++] = al::to_underlying(device
->getOutputMode1());
2562 case ALC_DEVICE_CLOCK_SOFT
:
2564 uint samplecount
, refcount
;
2565 nanoseconds basecount
;
2567 refcount
= dev
->waitForMix();
2568 basecount
= dev
->mClockBase
.load(std::memory_order_relaxed
);
2569 samplecount
= dev
->mSamplesDone
.load(std::memory_order_relaxed
);
2570 std::atomic_thread_fence(std::memory_order_acquire
);
2571 } while(refcount
!= dev
->mMixCount
.load(std::memory_order_relaxed
));
2572 basecount
+= nanoseconds
{seconds
{samplecount
}} / dev
->Frequency
;
2573 valuespan
[0] = basecount
.count();
2577 case ALC_DEVICE_LATENCY_SOFT
:
2578 valuespan
[0] = GetClockLatency(dev
.get(), dev
->Backend
.get()).Latency
.count();
2581 case ALC_DEVICE_CLOCK_LATENCY_SOFT
:
2583 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2586 ClockLatency clock
{GetClockLatency(dev
.get(), dev
->Backend
.get())};
2587 valuespan
[0] = clock
.ClockTime
.count();
2588 valuespan
[1] = clock
.Latency
.count();
2593 auto ivals
= std::vector
<int>(valuespan
.size());
2594 if(size_t got
{GetIntegerv(dev
.get(), pname
, ivals
)})
2595 std::copy_n(ivals
.cbegin(), got
, valuespan
.begin());
2601 ALC_API ALCboolean ALC_APIENTRY
alcIsExtensionPresent(ALCdevice
*device
, const ALCchar
*extName
) noexcept
2603 DeviceRef dev
{VerifyDevice(device
)};
2606 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2610 const std::string_view tofind
{extName
};
2611 const auto extlist
= dev
? std::string_view
{GetExtensionList()}
2612 : std::string_view
{GetNoDeviceExtList()};
2613 auto matchpos
= extlist
.find(tofind
);
2614 while(matchpos
!= std::string_view::npos
)
2616 const auto endpos
= matchpos
+ tofind
.size();
2617 if((matchpos
== 0 || std::isspace(extlist
[matchpos
-1]))
2618 && (endpos
== extlist
.size() || std::isspace(extlist
[endpos
])))
2620 matchpos
= extlist
.find(tofind
, matchpos
+1);
2626 ALCvoid
* ALC_APIENTRY
alcGetProcAddress2(ALCdevice
*device
, const ALCchar
*funcName
) noexcept
2627 { return alcGetProcAddress(device
, funcName
); }
2629 ALC_API ALCvoid
* ALC_APIENTRY
alcGetProcAddress(ALCdevice
*device
, const ALCchar
*funcName
) noexcept
2633 DeviceRef dev
{VerifyDevice(device
)};
2634 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2639 if(eax_g_is_enabled
)
2641 for(const auto &func
: eaxFunctions
)
2643 if(strcmp(func
.funcName
, funcName
) == 0)
2644 return func
.address
;
2648 for(const auto &func
: alcFunctions
)
2650 if(strcmp(func
.funcName
, funcName
) == 0)
2651 return func
.address
;
2657 ALC_API ALCenum ALC_APIENTRY
alcGetEnumValue(ALCdevice
*device
, const ALCchar
*enumName
) noexcept
2661 DeviceRef dev
{VerifyDevice(device
)};
2662 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2667 if(eax_g_is_enabled
)
2669 for(const auto &enm
: eaxEnumerations
)
2671 if(strcmp(enm
.enumName
, enumName
) == 0)
2676 for(const auto &enm
: alcEnumerations
)
2678 if(strcmp(enm
.enumName
, enumName
) == 0)
2686 ALC_API ALCcontext
* ALC_APIENTRY
alcCreateContext(ALCdevice
*device
, const ALCint
*attrList
) noexcept
2688 /* Explicitly hold the list lock while taking the StateLock in case the
2689 * device is asynchronously destroyed, to ensure this new context is
2690 * properly cleaned up after being made.
2692 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
2693 DeviceRef dev
{VerifyDevice(device
)};
2694 if(!dev
|| dev
->Type
== DeviceType::Capture
|| !dev
->Connected
.load(std::memory_order_relaxed
))
2697 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
2700 std::unique_lock
<std::mutex
> statelock
{dev
->StateLock
};
2703 dev
->LastError
.store(ALC_NO_ERROR
);
2705 const auto attrSpan
= SpanFromAttributeList(attrList
);
2706 ALCenum err
{UpdateDeviceParams(dev
.get(), attrSpan
)};
2707 if(err
!= ALC_NO_ERROR
)
2709 alcSetError(dev
.get(), err
);
2713 ContextFlagBitset ctxflags
{0};
2714 for(size_t i
{0};i
< attrSpan
.size();i
+=2)
2716 if(attrSpan
[i
] == ALC_CONTEXT_FLAGS_EXT
)
2718 ctxflags
= static_cast<ALuint
>(attrSpan
[i
+1]);
2723 auto context
= ContextRef
{new(std::nothrow
) ALCcontext
{dev
, ctxflags
}};
2726 alcSetError(dev
.get(), ALC_OUT_OF_MEMORY
);
2731 if(auto volopt
= dev
->configValue
<float>({}, "volume-adjust"))
2733 const float valf
{*volopt
};
2734 if(!std::isfinite(valf
))
2735 ERR("volume-adjust must be finite: %f\n", valf
);
2738 const float db
{std::clamp(valf
, -24.0f
, 24.0f
)};
2740 WARN("volume-adjust clamped: %f, range: +/-%f\n", valf
, 24.0f
);
2741 context
->mGainBoost
= std::pow(10.0f
, db
/20.0f
);
2742 TRACE("volume-adjust gain: %f\n", context
->mGainBoost
);
2747 using ContextArray
= al::FlexArray
<ContextBase
*>;
2749 /* Allocate a new context array, which holds 1 more than the current/
2752 auto *oldarray
= device
->mContexts
.load();
2753 auto newarray
= ContextArray::Create(oldarray
->size() + 1);
2755 /* Copy the current/old context handles to the new array, appending the
2758 auto iter
= std::copy(oldarray
->begin(), oldarray
->end(), newarray
->begin());
2759 *iter
= context
.get();
2761 /* Store the new context array in the device. Wait for any current mix
2762 * to finish before deleting the old array.
2764 auto prevarray
= dev
->mContexts
.exchange(std::move(newarray
));
2765 std::ignore
= dev
->waitForMix();
2771 auto iter
= std::lower_bound(ContextList
.cbegin(), ContextList
.cend(), context
.get());
2772 ContextList
.emplace(iter
, context
.get());
2776 if(ALeffectslot
*slot
{context
->mDefaultSlot
.get()})
2778 ALenum sloterr
{slot
->initEffect(0, ALCcontext::sDefaultEffect
.type
,
2779 ALCcontext::sDefaultEffect
.Props
, context
.get())};
2780 if(sloterr
== AL_NO_ERROR
)
2781 slot
->updateProps(context
.get());
2783 ERR("Failed to initialize the default effect\n");
2786 TRACE("Created context %p\n", voidp
{context
.get()});
2787 return context
.release();
2790 ALC_API
void ALC_APIENTRY
alcDestroyContext(ALCcontext
*context
) noexcept
2792 if(!gProcessRunning
)
2795 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
2796 auto iter
= std::lower_bound(ContextList
.begin(), ContextList
.end(), context
);
2797 if(iter
== ContextList
.end() || *iter
!= context
)
2800 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2804 /* Hold a reference to this context so it remains valid until the ListLock
2807 ContextRef ctx
{*iter
};
2808 ContextList
.erase(iter
);
2810 ALCdevice
*Device
{ctx
->mALDevice
.get()};
2812 std::lock_guard
<std::mutex
> statelock
{Device
->StateLock
};
2817 ALC_API
auto ALC_APIENTRY
alcGetCurrentContext() noexcept
-> ALCcontext
*
2819 ALCcontext
*Context
{ALCcontext::getThreadContext()};
2820 if(!Context
) Context
= ALCcontext::sGlobalContext
.load();
2824 /** Returns the currently active thread-local context. */
2825 ALC_API
auto ALC_APIENTRY
alcGetThreadContext() noexcept
-> ALCcontext
*
2826 { return ALCcontext::getThreadContext(); }
2828 ALC_API ALCboolean ALC_APIENTRY
alcMakeContextCurrent(ALCcontext
*context
) noexcept
2830 /* context must be valid or nullptr */
2834 ctx
= VerifyContext(context
);
2837 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2841 /* Release this reference (if any) to store it in the GlobalContext
2842 * pointer. Take ownership of the reference (if any) that was previously
2843 * stored there, and let the reference go.
2845 while(ALCcontext::sGlobalContextLock
.exchange(true, std::memory_order_acquire
)) {
2846 /* Wait to make sure another thread isn't getting or trying to change
2847 * the current context as its refcount is decremented.
2850 ctx
= ContextRef
{ALCcontext::sGlobalContext
.exchange(ctx
.release())};
2851 ALCcontext::sGlobalContextLock
.store(false, std::memory_order_release
);
2853 /* Take ownership of the thread-local context reference (if any), clearing
2854 * the storage to null.
2856 ctx
= ContextRef
{ALCcontext::getThreadContext()};
2857 if(ctx
) ALCcontext::setThreadContext(nullptr);
2858 /* Reset (decrement) the previous thread-local reference. */
2863 /** Makes the given context the active context for the current thread. */
2864 ALC_API ALCboolean ALC_APIENTRY
alcSetThreadContext(ALCcontext
*context
) noexcept
2866 /* context must be valid or nullptr */
2870 ctx
= VerifyContext(context
);
2873 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2877 /* context's reference count is already incremented */
2878 ContextRef old
{ALCcontext::getThreadContext()};
2879 ALCcontext::setThreadContext(ctx
.release());
2885 ALC_API ALCdevice
* ALC_APIENTRY
alcGetContextsDevice(ALCcontext
*Context
) noexcept
2887 ContextRef ctx
{VerifyContext(Context
)};
2890 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2893 return ctx
->mALDevice
.get();
2897 ALC_API ALCdevice
* ALC_APIENTRY
alcOpenDevice(const ALCchar
*deviceName
) noexcept
2901 if(!PlaybackFactory
)
2903 alcSetError(nullptr, ALC_INVALID_VALUE
);
2907 std::string_view devname
{deviceName
? deviceName
: ""};
2908 if(!devname
.empty())
2910 TRACE("Opening playback device \"%.*s\"\n", al::sizei(devname
), devname
.data());
2911 if(al::case_compare(devname
, GetDefaultName()) == 0
2913 /* Some old Windows apps hardcode these expecting OpenAL to use a
2914 * specific audio API, even when they're not enumerated. Creative's
2915 * router effectively ignores them too.
2917 || al::case_compare(devname
, "DirectSound3D"sv
) == 0
2918 || al::case_compare(devname
, "DirectSound"sv
) == 0
2919 || al::case_compare(devname
, "MMSYSTEM"sv
) == 0
2921 /* Some old Linux apps hardcode configuration strings that were
2922 * supported by the OpenAL SI. We can't really do anything useful
2923 * with them, so just ignore.
2925 || al::starts_with(devname
, "'("sv
)
2926 || al::case_compare(devname
, "openal-soft"sv
) == 0)
2930 const auto prefix
= GetDevicePrefix();
2931 if(!prefix
.empty() && devname
.size() > prefix
.size()
2932 && al::starts_with(devname
, prefix
))
2933 devname
= devname
.substr(prefix
.size());
2937 TRACE("Opening default playback device\n");
2939 const uint DefaultSends
{
2941 eax_g_is_enabled
? uint
{EAX_MAX_FXSLOTS
} :
2942 #endif // ALSOFT_EAX
2943 uint
{DefaultSendCount
}
2946 DeviceRef device
{new(std::nothrow
) ALCdevice
{DeviceType::Playback
}};
2949 WARN("Failed to create playback device handle\n");
2950 alcSetError(nullptr, ALC_OUT_OF_MEMORY
);
2954 /* Set output format */
2955 device
->FmtChans
= DevFmtChannelsDefault
;
2956 device
->FmtType
= DevFmtTypeDefault
;
2957 device
->Frequency
= DefaultOutputRate
;
2958 device
->UpdateSize
= DefaultUpdateSize
;
2959 device
->BufferSize
= DefaultUpdateSize
* DefaultNumUpdates
;
2961 device
->SourcesMax
= 256;
2962 device
->NumStereoSources
= 1;
2963 device
->NumMonoSources
= device
->SourcesMax
- device
->NumStereoSources
;
2964 device
->AuxiliaryEffectSlotMax
= 64;
2965 device
->NumAuxSends
= DefaultSends
;
2968 auto backend
= PlaybackFactory
->createBackend(device
.get(), BackendType::Playback
);
2969 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
2970 backend
->open(devname
);
2971 device
->mDeviceName
= std::string
{GetDevicePrefix()}+backend
->mDeviceName
;
2972 device
->Backend
= std::move(backend
);
2974 catch(al::backend_exception
&e
) {
2975 WARN("Failed to open playback device: %s\n", e
.what());
2976 alcSetError(nullptr, (e
.errorCode() == al::backend_error::OutOfMemory
)
2977 ? ALC_OUT_OF_MEMORY
: ALC_INVALID_VALUE
);
2981 auto checkopt
= [&device
](const char *envname
, const std::string_view optname
)
2983 if(auto optval
= al::getenv(envname
)) return optval
;
2984 return device
->configValue
<std::string
>("game_compat", optname
);
2986 if(auto overrideopt
= checkopt("__ALSOFT_VENDOR_OVERRIDE", "vendor-override"sv
))
2988 device
->mVendorOverride
= std::move(*overrideopt
);
2989 TRACE("Overriding vendor string: \"%s\"\n", device
->mVendorOverride
.c_str());
2991 if(auto overrideopt
= checkopt("__ALSOFT_VERSION_OVERRIDE", "version-override"sv
))
2993 device
->mVersionOverride
= std::move(*overrideopt
);
2994 TRACE("Overriding version string: \"%s\"\n", device
->mVersionOverride
.c_str());
2996 if(auto overrideopt
= checkopt("__ALSOFT_RENDERER_OVERRIDE", "renderer-override"sv
))
2998 device
->mRendererOverride
= std::move(*overrideopt
);
2999 TRACE("Overriding renderer string: \"%s\"\n", device
->mRendererOverride
.c_str());
3003 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
3004 auto iter
= std::lower_bound(DeviceList
.cbegin(), DeviceList
.cend(), device
.get());
3005 DeviceList
.emplace(iter
, device
.get());
3008 TRACE("Created device %p, \"%s\"\n", voidp
{device
.get()}, device
->mDeviceName
.c_str());
3009 return device
.release();
3012 ALC_API ALCboolean ALC_APIENTRY
alcCloseDevice(ALCdevice
*device
) noexcept
3014 if(!gProcessRunning
)
3017 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
3018 auto iter
= std::lower_bound(DeviceList
.begin(), DeviceList
.end(), device
);
3019 if(iter
== DeviceList
.end() || *iter
!= device
)
3021 alcSetError(nullptr, ALC_INVALID_DEVICE
);
3024 if((*iter
)->Type
== DeviceType::Capture
)
3026 alcSetError(*iter
, ALC_INVALID_DEVICE
);
3030 /* Erase the device, and any remaining contexts left on it, from their
3033 DeviceRef dev
{*iter
};
3034 DeviceList
.erase(iter
);
3036 std::unique_lock
<std::mutex
> statelock
{dev
->StateLock
};
3037 std::vector
<ContextRef
> orphanctxs
;
3038 for(ContextBase
*ctx
: *dev
->mContexts
.load())
3040 auto ctxiter
= std::lower_bound(ContextList
.begin(), ContextList
.end(), ctx
);
3041 if(ctxiter
!= ContextList
.end() && *ctxiter
== ctx
)
3043 orphanctxs
.emplace_back(*ctxiter
);
3044 ContextList
.erase(ctxiter
);
3049 for(ContextRef
&context
: orphanctxs
)
3051 WARN("Releasing orphaned context %p\n", voidp
{context
.get()});
3056 if(dev
->mDeviceState
== DeviceState::Playing
)
3058 dev
->Backend
->stop();
3059 dev
->mDeviceState
= DeviceState::Configured
;
3066 /************************************************
3067 * ALC capture functions
3068 ************************************************/
3069 ALC_API ALCdevice
* ALC_APIENTRY
alcCaptureOpenDevice(const ALCchar
*deviceName
, ALCuint frequency
, ALCenum format
, ALCsizei samples
) noexcept
3075 alcSetError(nullptr, ALC_INVALID_VALUE
);
3081 alcSetError(nullptr, ALC_INVALID_VALUE
);
3085 std::string_view devname
{deviceName
? deviceName
: ""};
3086 if(!devname
.empty())
3088 TRACE("Opening capture device \"%.*s\"\n", al::sizei(devname
), devname
.data());
3089 if(al::case_compare(devname
, GetDefaultName()) == 0
3090 || al::case_compare(devname
, "openal-soft"sv
) == 0)
3094 const auto prefix
= GetDevicePrefix();
3095 if(!prefix
.empty() && devname
.size() > prefix
.size()
3096 && al::starts_with(devname
, prefix
))
3097 devname
= devname
.substr(prefix
.size());
3101 TRACE("Opening default capture device\n");
3103 DeviceRef device
{new(std::nothrow
) ALCdevice
{DeviceType::Capture
}};
3106 WARN("Failed to create capture device handle\n");
3107 alcSetError(nullptr, ALC_OUT_OF_MEMORY
);
3111 auto decompfmt
= DecomposeDevFormat(format
);
3114 alcSetError(nullptr, ALC_INVALID_ENUM
);
3118 device
->Frequency
= frequency
;
3119 device
->FmtChans
= decompfmt
->chans
;
3120 device
->FmtType
= decompfmt
->type
;
3121 device
->Flags
.set(FrequencyRequest
);
3122 device
->Flags
.set(ChannelsRequest
);
3123 device
->Flags
.set(SampleTypeRequest
);
3125 device
->UpdateSize
= static_cast<uint
>(samples
);
3126 device
->BufferSize
= static_cast<uint
>(samples
);
3128 TRACE("Capture format: %s, %s, %uhz, %u / %u buffer\n", DevFmtChannelsString(device
->FmtChans
),
3129 DevFmtTypeString(device
->FmtType
), device
->Frequency
, device
->UpdateSize
,
3130 device
->BufferSize
);
3133 auto backend
= CaptureFactory
->createBackend(device
.get(), BackendType::Capture
);
3134 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
3135 backend
->open(devname
);
3136 device
->mDeviceName
= std::string
{GetDevicePrefix()}+backend
->mDeviceName
;
3137 device
->Backend
= std::move(backend
);
3139 catch(al::backend_exception
&e
) {
3140 WARN("Failed to open capture device: %s\n", e
.what());
3141 alcSetError(nullptr, (e
.errorCode() == al::backend_error::OutOfMemory
)
3142 ? ALC_OUT_OF_MEMORY
: ALC_INVALID_VALUE
);
3147 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
3148 auto iter
= std::lower_bound(DeviceList
.cbegin(), DeviceList
.cend(), device
.get());
3149 DeviceList
.emplace(iter
, device
.get());
3151 device
->mDeviceState
= DeviceState::Configured
;
3153 TRACE("Created capture device %p, \"%s\"\n", voidp
{device
.get()}, device
->mDeviceName
.c_str());
3154 return device
.release();
3157 ALC_API ALCboolean ALC_APIENTRY
alcCaptureCloseDevice(ALCdevice
*device
) noexcept
3159 if(!gProcessRunning
)
3162 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
3163 auto iter
= std::lower_bound(DeviceList
.begin(), DeviceList
.end(), device
);
3164 if(iter
== DeviceList
.end() || *iter
!= device
)
3166 alcSetError(nullptr, ALC_INVALID_DEVICE
);
3169 if((*iter
)->Type
!= DeviceType::Capture
)
3171 alcSetError(*iter
, ALC_INVALID_DEVICE
);
3175 DeviceRef dev
{*iter
};
3176 DeviceList
.erase(iter
);
3179 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3180 if(dev
->mDeviceState
== DeviceState::Playing
)
3182 dev
->Backend
->stop();
3183 dev
->mDeviceState
= DeviceState::Configured
;
3189 ALC_API
void ALC_APIENTRY
alcCaptureStart(ALCdevice
*device
) noexcept
3191 DeviceRef dev
{VerifyDevice(device
)};
3192 if(!dev
|| dev
->Type
!= DeviceType::Capture
)
3194 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3198 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3199 if(!dev
->Connected
.load(std::memory_order_acquire
)
3200 || dev
->mDeviceState
< DeviceState::Configured
)
3201 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3202 else if(dev
->mDeviceState
!= DeviceState::Playing
)
3205 auto backend
= dev
->Backend
.get();
3207 dev
->mDeviceState
= DeviceState::Playing
;
3209 catch(al::backend_exception
& e
) {
3210 ERR("%s\n", e
.what());
3211 dev
->handleDisconnect("%s", e
.what());
3212 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3217 ALC_API
void ALC_APIENTRY
alcCaptureStop(ALCdevice
*device
) noexcept
3219 DeviceRef dev
{VerifyDevice(device
)};
3220 if(!dev
|| dev
->Type
!= DeviceType::Capture
)
3221 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3224 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3225 if(dev
->mDeviceState
== DeviceState::Playing
)
3227 dev
->Backend
->stop();
3228 dev
->mDeviceState
= DeviceState::Configured
;
3233 ALC_API
void ALC_APIENTRY
alcCaptureSamples(ALCdevice
*device
, ALCvoid
*buffer
, ALCsizei samples
) noexcept
3235 DeviceRef dev
{VerifyDevice(device
)};
3236 if(!dev
|| dev
->Type
!= DeviceType::Capture
)
3238 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3242 if(samples
< 0 || (samples
> 0 && buffer
== nullptr))
3244 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3250 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3251 BackendBase
*backend
{dev
->Backend
.get()};
3253 const auto usamples
= static_cast<uint
>(samples
);
3254 if(usamples
> backend
->availableSamples())
3256 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3260 backend
->captureSamples(static_cast<std::byte
*>(buffer
), usamples
);
3264 /************************************************
3265 * ALC loopback functions
3266 ************************************************/
3268 /** Open a loopback device, for manual rendering. */
3269 ALC_API ALCdevice
* ALC_APIENTRY
alcLoopbackOpenDeviceSOFT(const ALCchar
*deviceName
) noexcept
3273 /* Make sure the device name, if specified, is us. */
3274 if(deviceName
&& strcmp(deviceName
, GetDefaultName()) != 0)
3276 alcSetError(nullptr, ALC_INVALID_VALUE
);
3280 const uint DefaultSends
{
3282 eax_g_is_enabled
? uint
{EAX_MAX_FXSLOTS
} :
3283 #endif // ALSOFT_EAX
3284 uint
{DefaultSendCount
}
3287 DeviceRef device
{new(std::nothrow
) ALCdevice
{DeviceType::Loopback
}};
3290 WARN("Failed to create loopback device handle\n");
3291 alcSetError(nullptr, ALC_OUT_OF_MEMORY
);
3295 device
->SourcesMax
= 256;
3296 device
->AuxiliaryEffectSlotMax
= 64;
3297 device
->NumAuxSends
= DefaultSends
;
3300 device
->BufferSize
= 0;
3301 device
->UpdateSize
= 0;
3303 device
->Frequency
= DefaultOutputRate
;
3304 device
->FmtChans
= DevFmtChannelsDefault
;
3305 device
->FmtType
= DevFmtTypeDefault
;
3307 device
->NumStereoSources
= 1;
3308 device
->NumMonoSources
= device
->SourcesMax
- device
->NumStereoSources
;
3311 auto backend
= LoopbackBackendFactory::getFactory().createBackend(device
.get(),
3312 BackendType::Playback
);
3313 backend
->open("Loopback");
3314 device
->mDeviceName
= std::string
{GetDevicePrefix()}+backend
->mDeviceName
;
3315 device
->Backend
= std::move(backend
);
3317 catch(al::backend_exception
&e
) {
3318 WARN("Failed to open loopback device: %s\n", e
.what());
3319 alcSetError(nullptr, (e
.errorCode() == al::backend_error::OutOfMemory
)
3320 ? ALC_OUT_OF_MEMORY
: ALC_INVALID_VALUE
);
3325 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
3326 auto iter
= std::lower_bound(DeviceList
.cbegin(), DeviceList
.cend(), device
.get());
3327 DeviceList
.emplace(iter
, device
.get());
3330 TRACE("Created loopback device %p\n", voidp
{device
.get()});
3331 return device
.release();
3335 * Determines if the loopback device supports the given format for rendering.
3337 ALC_API ALCboolean ALC_APIENTRY
alcIsRenderFormatSupportedSOFT(ALCdevice
*device
, ALCsizei freq
, ALCenum channels
, ALCenum type
) noexcept
3339 DeviceRef dev
{VerifyDevice(device
)};
3340 if(!dev
|| dev
->Type
!= DeviceType::Loopback
)
3341 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3343 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3346 if(DevFmtTypeFromEnum(type
).has_value() && DevFmtChannelsFromEnum(channels
).has_value()
3347 && freq
>= int{MinOutputRate
} && freq
<= int{MaxOutputRate
})
3355 * Renders some samples into a buffer, using the format last set by the
3356 * attributes given to alcCreateContext.
3358 #if defined(__GNUC__) && defined(__i386__)
3359 /* Needed on x86-32 even without SSE codegen, since the mixer may still use SSE
3360 * and GCC assumes the stack is aligned (x86-64 ABI guarantees alignment).
3362 [[gnu::force_align_arg_pointer
]]
3364 ALC_API
void ALC_APIENTRY
alcRenderSamplesSOFT(ALCdevice
*device
, ALCvoid
*buffer
, ALCsizei samples
) noexcept
3366 if(!device
|| device
->Type
!= DeviceType::Loopback
) UNLIKELY
3367 alcSetError(device
, ALC_INVALID_DEVICE
);
3368 else if(samples
< 0 || (samples
> 0 && buffer
== nullptr)) UNLIKELY
3369 alcSetError(device
, ALC_INVALID_VALUE
);
3371 device
->renderSamples(buffer
, static_cast<uint
>(samples
), device
->channelsFromFmt());
3375 /************************************************
3376 * ALC DSP pause/resume functions
3377 ************************************************/
3379 /** Pause the DSP to stop audio processing. */
3380 ALC_API
void ALC_APIENTRY
alcDevicePauseSOFT(ALCdevice
*device
) noexcept
3382 DeviceRef dev
{VerifyDevice(device
)};
3383 if(!dev
|| dev
->Type
!= DeviceType::Playback
)
3384 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3387 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3388 if(dev
->mDeviceState
== DeviceState::Playing
)
3390 dev
->Backend
->stop();
3391 dev
->mDeviceState
= DeviceState::Configured
;
3393 dev
->Flags
.set(DevicePaused
);
3397 /** Resume the DSP to restart audio processing. */
3398 ALC_API
void ALC_APIENTRY
alcDeviceResumeSOFT(ALCdevice
*device
) noexcept
3400 DeviceRef dev
{VerifyDevice(device
)};
3401 if(!dev
|| dev
->Type
!= DeviceType::Playback
)
3403 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3407 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3408 if(!dev
->Flags
.test(DevicePaused
))
3410 if(dev
->mDeviceState
< DeviceState::Configured
)
3412 WARN("Cannot resume unconfigured device\n");
3413 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3416 if(!dev
->Connected
.load())
3418 WARN("Cannot resume a disconnected device\n");
3419 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3422 dev
->Flags
.reset(DevicePaused
);
3423 if(dev
->mContexts
.load()->empty())
3427 auto backend
= dev
->Backend
.get();
3429 dev
->mDeviceState
= DeviceState::Playing
;
3431 catch(al::backend_exception
& e
) {
3432 ERR("%s\n", e
.what());
3433 dev
->handleDisconnect("%s", e
.what());
3434 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3437 TRACE("Post-resume: %s, %s, %uhz, %u / %u buffer\n",
3438 DevFmtChannelsString(dev
->FmtChans
), DevFmtTypeString(dev
->FmtType
),
3439 dev
->Frequency
, dev
->UpdateSize
, dev
->BufferSize
);
3443 /************************************************
3444 * ALC HRTF functions
3445 ************************************************/
3447 /** Gets a string parameter at the given index. */
3448 ALC_API
const ALCchar
* ALC_APIENTRY
alcGetStringiSOFT(ALCdevice
*device
, ALCenum paramName
, ALCsizei index
) noexcept
3450 DeviceRef dev
{VerifyDevice(device
)};
3451 if(!dev
|| dev
->Type
== DeviceType::Capture
)
3452 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3453 else switch(paramName
)
3455 case ALC_HRTF_SPECIFIER_SOFT
:
3456 if(index
>= 0 && static_cast<uint
>(index
) < dev
->mHrtfList
.size())
3457 return dev
->mHrtfList
[static_cast<uint
>(index
)].c_str();
3458 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3462 alcSetError(dev
.get(), ALC_INVALID_ENUM
);
3469 /** Resets the given device output, using the specified attribute list. */
3470 ALC_API ALCboolean ALC_APIENTRY
alcResetDeviceSOFT(ALCdevice
*device
, const ALCint
*attribs
) noexcept
3472 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
3473 DeviceRef dev
{VerifyDevice(device
)};
3474 if(!dev
|| dev
->Type
== DeviceType::Capture
)
3477 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3480 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3483 /* Force the backend to stop mixing first since we're resetting. Also reset
3484 * the connected state so lost devices can attempt recover.
3486 if(dev
->mDeviceState
== DeviceState::Playing
)
3488 dev
->Backend
->stop();
3489 dev
->mDeviceState
= DeviceState::Configured
;
3492 return ResetDeviceParams(dev
.get(), SpanFromAttributeList(attribs
)) ? ALC_TRUE
: ALC_FALSE
;
3496 /************************************************
3497 * ALC device reopen functions
3498 ************************************************/
3500 /** Reopens the given device output, using the specified name and attribute list. */
3501 FORCE_ALIGN ALCboolean ALC_APIENTRY
alcReopenDeviceSOFT(ALCdevice
*device
,
3502 const ALCchar
*deviceName
, const ALCint
*attribs
) noexcept
3504 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
3505 DeviceRef dev
{VerifyDevice(device
)};
3506 if(!dev
|| dev
->Type
!= DeviceType::Playback
)
3509 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3512 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3514 std::string_view devname
{deviceName
? deviceName
: ""};
3515 if(!devname
.empty())
3517 if(devname
.length() >= size_t{std::numeric_limits
<int>::max()})
3519 ERR("Device name too long (%zu >= %d)\n", devname
.length(),
3520 std::numeric_limits
<int>::max());
3521 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3524 if(al::case_compare(devname
, GetDefaultName()) == 0)
3528 const auto prefix
= GetDevicePrefix();
3529 if(!prefix
.empty() && devname
.size() > prefix
.size()
3530 && al::starts_with(devname
, prefix
))
3531 devname
= devname
.substr(prefix
.size());
3535 /* Force the backend device to stop first since we're opening another one. */
3536 const bool wasPlaying
{dev
->mDeviceState
== DeviceState::Playing
};
3539 dev
->Backend
->stop();
3540 dev
->mDeviceState
= DeviceState::Configured
;
3543 BackendPtr newbackend
;
3545 newbackend
= PlaybackFactory
->createBackend(dev
.get(), BackendType::Playback
);
3546 newbackend
->open(devname
);
3548 catch(al::backend_exception
&e
) {
3550 newbackend
= nullptr;
3552 WARN("Failed to reopen playback device: %s\n", e
.what());
3553 alcSetError(dev
.get(), (e
.errorCode() == al::backend_error::OutOfMemory
)
3554 ? ALC_OUT_OF_MEMORY
: ALC_INVALID_VALUE
);
3556 if(dev
->Connected
.load(std::memory_order_relaxed
) && wasPlaying
)
3559 auto backend
= dev
->Backend
.get();
3561 dev
->mDeviceState
= DeviceState::Playing
;
3563 catch(al::backend_exception
&be
) {
3564 ERR("%s\n", be
.what());
3565 dev
->handleDisconnect("%s", be
.what());
3571 dev
->mDeviceName
= std::string
{GetDevicePrefix()}+newbackend
->mDeviceName
;
3572 dev
->Backend
= std::move(newbackend
);
3573 dev
->mDeviceState
= DeviceState::Unprepared
;
3574 TRACE("Reopened device %p, \"%s\"\n", voidp
{dev
.get()}, dev
->mDeviceName
.c_str());
3576 std::string
{}.swap(dev
->mVendorOverride
);
3577 std::string
{}.swap(dev
->mVersionOverride
);
3578 std::string
{}.swap(dev
->mRendererOverride
);
3579 auto checkopt
= [&dev
](const char *envname
, const std::string_view optname
)
3581 if(auto optval
= al::getenv(envname
)) return optval
;
3582 return dev
->configValue
<std::string
>("game_compat", optname
);
3584 if(auto overrideopt
= checkopt("__ALSOFT_VENDOR_OVERRIDE", "vendor-override"sv
))
3586 dev
->mVendorOverride
= std::move(*overrideopt
);
3587 TRACE("Overriding vendor string: \"%s\"\n", dev
->mVendorOverride
.c_str());
3589 if(auto overrideopt
= checkopt("__ALSOFT_VERSION_OVERRIDE", "version-override"sv
))
3591 dev
->mVersionOverride
= std::move(*overrideopt
);
3592 TRACE("Overriding version string: \"%s\"\n", dev
->mVersionOverride
.c_str());
3594 if(auto overrideopt
= checkopt("__ALSOFT_RENDERER_OVERRIDE", "renderer-override"sv
))
3596 dev
->mRendererOverride
= std::move(*overrideopt
);
3597 TRACE("Overriding renderer string: \"%s\"\n", dev
->mRendererOverride
.c_str());
3600 /* Always return true even if resetting fails. It shouldn't fail, but this
3601 * is primarily to avoid confusion by the app seeing the function return
3602 * false while the device is on the new output anyway. We could try to
3603 * restore the old backend if this fails, but the configuration would be
3604 * changed with the new backend and would need to be reset again with the
3605 * old one, and the provided attributes may not be appropriate or desirable
3606 * for the old device.
3608 * In this way, we essentially act as if the function succeeded, but
3609 * immediately disconnects following it.
3611 ResetDeviceParams(dev
.get(), SpanFromAttributeList(attribs
));
3615 /************************************************
3616 * ALC event query functions
3617 ************************************************/
3619 FORCE_ALIGN ALCenum ALC_APIENTRY
alcEventIsSupportedSOFT(ALCenum eventType
, ALCenum deviceType
) noexcept
3621 auto etype
= alc::GetEventType(eventType
);
3624 WARN("Invalid event type: 0x%04x\n", eventType
);
3625 alcSetError(nullptr, ALC_INVALID_ENUM
);
3629 auto supported
= alc::EventSupport::NoSupport
;
3632 case ALC_PLAYBACK_DEVICE_SOFT
:
3634 supported
= PlaybackFactory
->queryEventSupport(*etype
, BackendType::Playback
);
3635 return al::to_underlying(supported
);
3637 case ALC_CAPTURE_DEVICE_SOFT
:
3639 supported
= CaptureFactory
->queryEventSupport(*etype
, BackendType::Capture
);
3640 return al::to_underlying(supported
);
3642 WARN("Invalid device type: 0x%04x\n", deviceType
);
3643 alcSetError(nullptr, ALC_INVALID_ENUM
);