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 <type_traits>
64 #include "al/auxeffectslot.h"
65 #include "al/buffer.h"
67 #include "al/effect.h"
68 #include "al/filter.h"
69 #include "al/listener.h"
70 #include "al/source.h"
71 #include "alc/events.h"
75 #include "alnumeric.h"
81 #include "core/ambidefs.h"
82 #include "core/bformatdec.h"
83 #include "core/bs2b.h"
84 #include "core/context.h"
85 #include "core/cpu_caps.h"
86 #include "core/devformat.h"
87 #include "core/device.h"
88 #include "core/effectslot.h"
89 #include "core/except.h"
90 #include "core/helpers.h"
91 #include "core/mastering.h"
92 #include "core/mixer/hrtfdefs.h"
93 #include "core/fpu_ctrl.h"
94 #include "core/front_stablizer.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/wave.h"
161 #include "al/eax/globals.h"
162 #include "al/eax/x_ram.h"
166 /************************************************
167 * Library initialization
168 ************************************************/
169 #if defined(_WIN32) && !defined(AL_LIBTYPE_STATIC)
170 BOOL APIENTRY
DllMain(HINSTANCE module
, DWORD reason
, LPVOID
/*reserved*/)
174 case DLL_PROCESS_ATTACH
:
175 /* Pin the DLL so we won't get unloaded until the process terminates */
176 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN
| GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
177 reinterpret_cast<WCHAR
*>(module
), &module
);
186 using namespace std::string_view_literals
;
187 using std::chrono::seconds
;
188 using std::chrono::nanoseconds
;
191 using float2
= std::array
<float,2>;
194 /************************************************
196 ************************************************/
199 BackendFactory
& (*getFactory
)();
202 std::array BackendList
{
204 BackendInfo
{"pipewire", PipeWireBackendFactory::getFactory
},
206 #ifdef HAVE_PULSEAUDIO
207 BackendInfo
{"pulse", PulseBackendFactory::getFactory
},
210 BackendInfo
{"wasapi", WasapiBackendFactory::getFactory
},
212 #ifdef HAVE_COREAUDIO
213 BackendInfo
{"core", CoreAudioBackendFactory::getFactory
},
216 BackendInfo
{"oboe", OboeBackendFactory::getFactory
},
219 BackendInfo
{"opensl", OSLBackendFactory::getFactory
},
222 BackendInfo
{"alsa", AlsaBackendFactory::getFactory
},
225 BackendInfo
{"solaris", SolarisBackendFactory::getFactory
},
228 BackendInfo
{"sndio", SndIOBackendFactory::getFactory
},
231 BackendInfo
{"oss", OSSBackendFactory::getFactory
},
234 BackendInfo
{"jack", JackBackendFactory::getFactory
},
237 BackendInfo
{"dsound", DSoundBackendFactory::getFactory
},
240 BackendInfo
{"winmm", WinMMBackendFactory::getFactory
},
242 #ifdef HAVE_PORTAUDIO
243 BackendInfo
{"port", PortBackendFactory::getFactory
},
246 BackendInfo
{"sdl2", SDL2BackendFactory::getFactory
},
249 BackendInfo
{"null", NullBackendFactory::getFactory
},
251 BackendInfo
{"wave", WaveBackendFactory::getFactory
},
255 BackendFactory
*PlaybackFactory
{};
256 BackendFactory
*CaptureFactory
{};
259 [[nodiscard
]] constexpr auto GetNoErrorString() noexcept
{ return "No Error"; }
260 [[nodiscard
]] constexpr auto GetInvalidDeviceString() noexcept
{ return "Invalid Device"; }
261 [[nodiscard
]] constexpr auto GetInvalidContextString() noexcept
{ return "Invalid Context"; }
262 [[nodiscard
]] constexpr auto GetInvalidEnumString() noexcept
{ return "Invalid Enum"; }
263 [[nodiscard
]] constexpr auto GetInvalidValueString() noexcept
{ return "Invalid Value"; }
264 [[nodiscard
]] constexpr auto GetOutOfMemoryString() noexcept
{ return "Out of Memory"; }
266 [[nodiscard
]] constexpr auto GetDefaultName() noexcept
{ return "OpenAL Soft\0"; }
268 /************************************************
270 ************************************************/
272 /* Enumerated device names */
273 std::string alcAllDevicesList
;
274 std::string alcCaptureDeviceList
;
276 /* Default is always the first in the list */
277 std::string alcDefaultAllDevicesSpecifier
;
278 std::string alcCaptureDefaultDeviceSpecifier
;
280 std::atomic
<ALCenum
> LastNullDeviceError
{ALC_NO_ERROR
};
282 /* Flag to trap ALC device errors */
283 bool TrapALCError
{false};
285 /* One-time configuration init control */
286 std::once_flag alc_config_once
{};
288 /* Flag to specify if alcSuspendContext/alcProcessContext should defer/process
291 bool SuspendDefers
{true};
293 /* Initial seed for dithering. */
294 constexpr uint DitherRNGSeed
{22222u};
297 /************************************************
299 ************************************************/
300 [[nodiscard
]] constexpr auto GetNoDeviceExtList() noexcept
-> std::string_view
302 return "ALC_ENUMERATE_ALL_EXT "
303 "ALC_ENUMERATION_EXT "
305 "ALC_EXTX_direct_context "
307 "ALC_EXT_thread_local_context "
309 "ALC_SOFT_loopback_bformat "
310 "ALC_SOFT_reopen_device "
311 "ALC_SOFT_system_events"sv
;
313 [[nodiscard
]] constexpr auto GetExtensionList() noexcept
-> std::string_view
315 return "ALC_ENUMERATE_ALL_EXT "
316 "ALC_ENUMERATION_EXT "
320 "ALC_EXTX_direct_context "
321 "ALC_EXT_disconnect "
323 "ALC_EXT_thread_local_context "
324 "ALC_SOFT_device_clock "
327 "ALC_SOFT_loopback_bformat "
328 "ALC_SOFT_output_limiter "
329 "ALC_SOFT_output_mode "
330 "ALC_SOFT_pause_device "
331 "ALC_SOFT_reopen_device "
332 "ALC_SOFT_system_events"sv
;
335 constexpr int alcMajorVersion
{1};
336 constexpr int alcMinorVersion
{1};
338 constexpr int alcEFXMajorVersion
{1};
339 constexpr int alcEFXMinorVersion
{0};
342 using DeviceRef
= al::intrusive_ptr
<ALCdevice
>;
345 /************************************************
347 ************************************************/
348 std::vector
<ALCdevice
*> DeviceList
;
349 std::vector
<ALCcontext
*> ContextList
;
351 std::recursive_mutex ListLock
;
354 void alc_initconfig()
356 if(auto loglevel
= al::getenv("ALSOFT_LOGLEVEL"))
358 long lvl
= strtol(loglevel
->c_str(), nullptr, 0);
359 if(lvl
>= static_cast<long>(LogLevel::Trace
))
360 gLogLevel
= LogLevel::Trace
;
361 else if(lvl
<= static_cast<long>(LogLevel::Disable
))
362 gLogLevel
= LogLevel::Disable
;
364 gLogLevel
= static_cast<LogLevel
>(lvl
);
368 if(const auto logfile
= al::getenv(L
"ALSOFT_LOGFILE"))
370 FILE *logf
{_wfopen(logfile
->c_str(), L
"wt")};
371 if(logf
) gLogFile
= logf
;
374 auto u8name
= wstr_to_utf8(*logfile
);
375 ERR("Failed to open log file '%s'\n", u8name
.c_str());
379 if(const auto logfile
= al::getenv("ALSOFT_LOGFILE"))
381 FILE *logf
{fopen(logfile
->c_str(), "wt")};
382 if(logf
) gLogFile
= logf
;
383 else ERR("Failed to open log file '%s'\n", logfile
->c_str());
387 TRACE("Initializing library v%s-%s %s\n", ALSOFT_VERSION
, ALSOFT_GIT_COMMIT_HASH
,
391 if(std::size(BackendList
) < 1)
395 const al::span
<const BackendInfo
> infos
{BackendList
};
396 names
= infos
[0].name
;
397 for(const auto &backend
: infos
.subspan
<1>())
400 names
+= backend
.name
;
403 TRACE("Supported backends: %s\n", names
.c_str());
407 if(auto suspendmode
= al::getenv("__ALSOFT_SUSPEND_CONTEXT"))
409 if(al::case_compare(*suspendmode
, "ignore"sv
) == 0)
411 SuspendDefers
= false;
412 TRACE("Selected context suspend behavior, \"ignore\"\n");
415 ERR("Unhandled context suspend behavior setting: \"%s\"\n", suspendmode
->c_str());
419 #if defined(HAVE_SSE4_1)
420 capfilter
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
| CPU_CAP_SSE4_1
;
421 #elif defined(HAVE_SSE3)
422 capfilter
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
;
423 #elif defined(HAVE_SSE2)
424 capfilter
|= CPU_CAP_SSE
| CPU_CAP_SSE2
;
425 #elif defined(HAVE_SSE)
426 capfilter
|= CPU_CAP_SSE
;
429 capfilter
|= CPU_CAP_NEON
;
431 if(auto cpuopt
= ConfigValueStr({}, {}, "disable-cpu-exts"sv
))
433 std::string_view cpulist
{*cpuopt
};
434 if(al::case_compare(cpulist
, "all"sv
) == 0)
436 else while(!cpulist
.empty())
438 auto nextpos
= std::min(cpulist
.find(','), cpulist
.size());
439 auto entry
= cpulist
.substr(0, nextpos
);
441 while(nextpos
< cpulist
.size() && cpulist
[nextpos
] == ',')
443 cpulist
.remove_prefix(nextpos
);
445 while(!entry
.empty() && std::isspace(entry
.front()))
446 entry
.remove_prefix(1);
447 while(!entry
.empty() && std::isspace(entry
.back()))
448 entry
.remove_suffix(1);
452 if(al::case_compare(entry
, "sse"sv
) == 0)
453 capfilter
&= ~CPU_CAP_SSE
;
454 else if(al::case_compare(entry
, "sse2"sv
) == 0)
455 capfilter
&= ~CPU_CAP_SSE2
;
456 else if(al::case_compare(entry
, "sse3"sv
) == 0)
457 capfilter
&= ~CPU_CAP_SSE3
;
458 else if(al::case_compare(entry
, "sse4.1"sv
) == 0)
459 capfilter
&= ~CPU_CAP_SSE4_1
;
460 else if(al::case_compare(entry
, "neon"sv
) == 0)
461 capfilter
&= ~CPU_CAP_NEON
;
463 WARN("Invalid CPU extension \"%.*s\"\n", std::abs(static_cast<int>(entry
.size())),
468 if(auto cpuopt
= GetCPUInfo())
470 if(!cpuopt
->mVendor
.empty() || !cpuopt
->mName
.empty())
472 TRACE("Vendor ID: \"%s\"\n", cpuopt
->mVendor
.c_str());
473 TRACE("Name: \"%s\"\n", cpuopt
->mName
.c_str());
475 const int caps
{cpuopt
->mCaps
};
476 TRACE("Extensions:%s%s%s%s%s%s\n",
477 ((capfilter
&CPU_CAP_SSE
) ? ((caps
&CPU_CAP_SSE
) ? " +SSE" : " -SSE") : ""),
478 ((capfilter
&CPU_CAP_SSE2
) ? ((caps
&CPU_CAP_SSE2
) ? " +SSE2" : " -SSE2") : ""),
479 ((capfilter
&CPU_CAP_SSE3
) ? ((caps
&CPU_CAP_SSE3
) ? " +SSE3" : " -SSE3") : ""),
480 ((capfilter
&CPU_CAP_SSE4_1
) ? ((caps
&CPU_CAP_SSE4_1
) ? " +SSE4.1" : " -SSE4.1") : ""),
481 ((capfilter
&CPU_CAP_NEON
) ? ((caps
&CPU_CAP_NEON
) ? " +NEON" : " -NEON") : ""),
482 ((!capfilter
) ? " -none-" : ""));
483 CPUCapFlags
= caps
& capfilter
;
486 if(auto priopt
= ConfigValueInt({}, {}, "rt-prio"sv
))
487 RTPrioLevel
= *priopt
;
488 if(auto limopt
= ConfigValueBool({}, {}, "rt-time-limit"sv
))
489 AllowRTTimeLimit
= *limopt
;
492 CompatFlagBitset compatflags
{};
493 auto checkflag
= [](const char *envname
, const std::string_view optname
) -> bool
495 if(auto optval
= al::getenv(envname
))
497 return al::case_compare(*optval
, "true"sv
) == 0
498 || strtol(optval
->c_str(), nullptr, 0) == 1;
500 return GetConfigValueBool({}, "game_compat", optname
, false);
502 sBufferSubDataCompat
= checkflag("__ALSOFT_ENABLE_SUB_DATA_EXT", "enable-sub-data-ext"sv
);
503 compatflags
.set(CompatFlags::ReverseX
, checkflag("__ALSOFT_REVERSE_X", "reverse-x"sv
));
504 compatflags
.set(CompatFlags::ReverseY
, checkflag("__ALSOFT_REVERSE_Y", "reverse-y"sv
));
505 compatflags
.set(CompatFlags::ReverseZ
, checkflag("__ALSOFT_REVERSE_Z", "reverse-z"sv
));
507 aluInit(compatflags
, ConfigValueFloat({}, "game_compat"sv
, "nfc-scale"sv
).value_or(1.0f
));
509 Voice::InitMixer(ConfigValueStr({}, {}, "resampler"sv
));
511 if(auto uhjfiltopt
= ConfigValueStr({}, "uhj"sv
, "decode-filter"sv
))
513 if(al::case_compare(*uhjfiltopt
, "fir256"sv
) == 0)
514 UhjDecodeQuality
= UhjQualityType::FIR256
;
515 else if(al::case_compare(*uhjfiltopt
, "fir512"sv
) == 0)
516 UhjDecodeQuality
= UhjQualityType::FIR512
;
517 else if(al::case_compare(*uhjfiltopt
, "iir"sv
) == 0)
518 UhjDecodeQuality
= UhjQualityType::IIR
;
520 WARN("Unsupported uhj/decode-filter: %s\n", uhjfiltopt
->c_str());
522 if(auto uhjfiltopt
= ConfigValueStr({}, "uhj"sv
, "encode-filter"sv
))
524 if(al::case_compare(*uhjfiltopt
, "fir256"sv
) == 0)
525 UhjEncodeQuality
= UhjQualityType::FIR256
;
526 else if(al::case_compare(*uhjfiltopt
, "fir512"sv
) == 0)
527 UhjEncodeQuality
= UhjQualityType::FIR512
;
528 else if(al::case_compare(*uhjfiltopt
, "iir"sv
) == 0)
529 UhjEncodeQuality
= UhjQualityType::IIR
;
531 WARN("Unsupported uhj/encode-filter: %s\n", uhjfiltopt
->c_str());
534 if(auto traperr
= al::getenv("ALSOFT_TRAP_ERROR"); traperr
535 && (al::case_compare(*traperr
, "true"sv
) == 0
536 || std::strtol(traperr
->c_str(), nullptr, 0) == 1))
543 traperr
= al::getenv("ALSOFT_TRAP_AL_ERROR");
545 TrapALError
= al::case_compare(*traperr
, "true"sv
) == 0
546 || strtol(traperr
->c_str(), nullptr, 0) == 1;
548 TrapALError
= GetConfigValueBool({}, {}, "trap-al-error"sv
, false);
550 traperr
= al::getenv("ALSOFT_TRAP_ALC_ERROR");
552 TrapALCError
= al::case_compare(*traperr
, "true"sv
) == 0
553 || strtol(traperr
->c_str(), nullptr, 0) == 1;
555 TrapALCError
= GetConfigValueBool({}, {}, "trap-alc-error"sv
, false);
558 if(auto boostopt
= ConfigValueFloat({}, "reverb"sv
, "boost"sv
))
560 const float valf
{std::isfinite(*boostopt
) ? clampf(*boostopt
, -24.0f
, 24.0f
) : 0.0f
};
561 ReverbBoost
*= std::pow(10.0f
, valf
/ 20.0f
);
564 auto BackendListEnd
= std::end(BackendList
);
565 auto devopt
= al::getenv("ALSOFT_DRIVERS");
566 if(!devopt
) devopt
= ConfigValueStr({}, {}, "drivers"sv
);
569 auto backendlist_cur
= std::begin(BackendList
);
572 std::string_view drvlist
{*devopt
};
573 while(!drvlist
.empty())
575 auto nextpos
= std::min(drvlist
.find(','), drvlist
.size());
576 auto entry
= drvlist
.substr(0, nextpos
);
579 if(nextpos
< drvlist
.size())
582 while(nextpos
< drvlist
.size() && drvlist
[nextpos
] == ',')
585 drvlist
.remove_prefix(nextpos
);
587 while(!entry
.empty() && std::isspace(entry
.front()))
588 entry
.remove_prefix(1);
589 const bool delitem
{!entry
.empty() && entry
.front() == '-'};
590 if(delitem
) entry
.remove_prefix(1);
592 while(!entry
.empty() && std::isspace(entry
.back()))
593 entry
.remove_suffix(1);
598 /* HACK: For backwards compatibility, convert backend references of
599 * mmdevapi to wasapi. This should eventually be removed.
601 if(entry
== "mmdevapi"sv
)
605 auto find_backend
= [entry
](const BackendInfo
&backend
) -> bool
606 { return entry
== backend
.name
; };
607 auto this_backend
= std::find_if(std::begin(BackendList
), BackendListEnd
,
610 if(this_backend
== BackendListEnd
)
614 BackendListEnd
= std::move(this_backend
+1, BackendListEnd
, this_backend
);
616 backendlist_cur
= std::rotate(backendlist_cur
, this_backend
, this_backend
+1);
620 BackendListEnd
= backendlist_cur
;
623 auto init_backend
= [](BackendInfo
&backend
) -> void
625 if(PlaybackFactory
&& CaptureFactory
)
628 BackendFactory
&factory
= backend
.getFactory();
631 WARN("Failed to initialize backend \"%s\"\n", backend
.name
);
635 TRACE("Initialized backend \"%s\"\n", backend
.name
);
636 if(!PlaybackFactory
&& factory
.querySupport(BackendType::Playback
))
638 PlaybackFactory
= &factory
;
639 TRACE("Added \"%s\" for playback\n", backend
.name
);
641 if(!CaptureFactory
&& factory
.querySupport(BackendType::Capture
))
643 CaptureFactory
= &factory
;
644 TRACE("Added \"%s\" for capture\n", backend
.name
);
647 std::for_each(std::begin(BackendList
), BackendListEnd
, init_backend
);
649 LoopbackBackendFactory::getFactory().init();
652 WARN("No playback backend available!\n");
654 WARN("No capture backend available!\n");
656 if(auto exclopt
= ConfigValueStr({}, {}, "excludefx"sv
))
658 std::string_view exclude
{*exclopt
};
659 while(!exclude
.empty())
661 const auto nextpos
= exclude
.find(',');
662 const auto entry
= exclude
.substr(0, nextpos
);
663 exclude
.remove_prefix((nextpos
< exclude
.size()) ? nextpos
+1 : exclude
.size());
665 std::for_each(gEffectList
.cbegin(), gEffectList
.cend(),
666 [entry
](const EffectList
&effectitem
) noexcept
668 if(entry
== std::data(effectitem
.name
))
669 DisabledEffects
.set(effectitem
.type
);
674 InitEffect(&ALCcontext::sDefaultEffect
);
675 auto defrevopt
= al::getenv("ALSOFT_DEFAULT_REVERB");
676 if(!defrevopt
) defrevopt
= ConfigValueStr({}, {}, "default-reverb"sv
);
677 if(defrevopt
) LoadReverbPreset(*defrevopt
, &ALCcontext::sDefaultEffect
);
681 if(const auto eax_enable_opt
= ConfigValueBool({}, "eax", "enable"))
683 eax_g_is_enabled
= *eax_enable_opt
;
684 if(!eax_g_is_enabled
)
685 TRACE("%s\n", "EAX disabled by a configuration.");
688 eax_g_is_enabled
= true;
690 if((DisabledEffects
.test(EAXREVERB_EFFECT
) || DisabledEffects
.test(CHORUS_EFFECT
))
693 eax_g_is_enabled
= false;
694 TRACE("EAX disabled because %s disabled.\n",
695 (DisabledEffects
.test(EAXREVERB_EFFECT
) && DisabledEffects
.test(CHORUS_EFFECT
))
696 ? "EAXReverb and Chorus are" :
697 DisabledEffects
.test(EAXREVERB_EFFECT
) ? "EAXReverb is" :
698 DisabledEffects
.test(CHORUS_EFFECT
) ? "Chorus is" : "");
703 inline void InitConfig()
704 { std::call_once(alc_config_once
, [](){alc_initconfig();}); }
707 /************************************************
709 ************************************************/
710 void ProbeAllDevicesList()
714 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
716 decltype(alcAllDevicesList
){}.swap(alcAllDevicesList
);
719 std::string names
{PlaybackFactory
->probe(BackendType::Playback
)};
720 if(names
.empty()) names
+= '\0';
721 names
.swap(alcAllDevicesList
);
724 void ProbeCaptureDeviceList()
728 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
730 decltype(alcCaptureDeviceList
){}.swap(alcCaptureDeviceList
);
733 std::string names
{CaptureFactory
->probe(BackendType::Capture
)};
734 if(names
.empty()) names
+= '\0';
735 names
.swap(alcCaptureDeviceList
);
740 struct DevFmtPair
{ DevFmtChannels chans
; DevFmtType type
; };
741 std::optional
<DevFmtPair
> DecomposeDevFormat(ALenum format
)
745 DevFmtChannels channels
;
748 static constexpr std::array list
{
749 FormatType
{AL_FORMAT_MONO8
, DevFmtMono
, DevFmtUByte
},
750 FormatType
{AL_FORMAT_MONO16
, DevFmtMono
, DevFmtShort
},
751 FormatType
{AL_FORMAT_MONO_I32
, DevFmtMono
, DevFmtInt
},
752 FormatType
{AL_FORMAT_MONO_FLOAT32
, DevFmtMono
, DevFmtFloat
},
754 FormatType
{AL_FORMAT_STEREO8
, DevFmtStereo
, DevFmtUByte
},
755 FormatType
{AL_FORMAT_STEREO16
, DevFmtStereo
, DevFmtShort
},
756 FormatType
{AL_FORMAT_STEREO_I32
, DevFmtStereo
, DevFmtInt
},
757 FormatType
{AL_FORMAT_STEREO_FLOAT32
, DevFmtStereo
, DevFmtFloat
},
759 FormatType
{AL_FORMAT_QUAD8
, DevFmtQuad
, DevFmtUByte
},
760 FormatType
{AL_FORMAT_QUAD16
, DevFmtQuad
, DevFmtShort
},
761 FormatType
{AL_FORMAT_QUAD32
, DevFmtQuad
, DevFmtFloat
},
762 FormatType
{AL_FORMAT_QUAD_I32
, DevFmtQuad
, DevFmtInt
},
763 FormatType
{AL_FORMAT_QUAD_FLOAT32
, DevFmtQuad
, DevFmtFloat
},
765 FormatType
{AL_FORMAT_51CHN8
, DevFmtX51
, DevFmtUByte
},
766 FormatType
{AL_FORMAT_51CHN16
, DevFmtX51
, DevFmtShort
},
767 FormatType
{AL_FORMAT_51CHN32
, DevFmtX51
, DevFmtFloat
},
768 FormatType
{AL_FORMAT_51CHN_I32
, DevFmtX51
, DevFmtInt
},
769 FormatType
{AL_FORMAT_51CHN_FLOAT32
, DevFmtX51
, DevFmtFloat
},
771 FormatType
{AL_FORMAT_61CHN8
, DevFmtX61
, DevFmtUByte
},
772 FormatType
{AL_FORMAT_61CHN16
, DevFmtX61
, DevFmtShort
},
773 FormatType
{AL_FORMAT_61CHN32
, DevFmtX61
, DevFmtFloat
},
774 FormatType
{AL_FORMAT_61CHN_I32
, DevFmtX61
, DevFmtInt
},
775 FormatType
{AL_FORMAT_61CHN_FLOAT32
, DevFmtX61
, DevFmtFloat
},
777 FormatType
{AL_FORMAT_71CHN8
, DevFmtX71
, DevFmtUByte
},
778 FormatType
{AL_FORMAT_71CHN16
, DevFmtX71
, DevFmtShort
},
779 FormatType
{AL_FORMAT_71CHN32
, DevFmtX71
, DevFmtFloat
},
780 FormatType
{AL_FORMAT_71CHN_I32
, DevFmtX71
, DevFmtInt
},
781 FormatType
{AL_FORMAT_71CHN_FLOAT32
, DevFmtX71
, DevFmtFloat
},
784 for(const auto &item
: list
)
786 if(item
.format
== format
)
787 return DevFmtPair
{item
.channels
, item
.type
};
793 std::optional
<DevFmtType
> DevFmtTypeFromEnum(ALCenum type
)
797 case ALC_BYTE_SOFT
: return DevFmtByte
;
798 case ALC_UNSIGNED_BYTE_SOFT
: return DevFmtUByte
;
799 case ALC_SHORT_SOFT
: return DevFmtShort
;
800 case ALC_UNSIGNED_SHORT_SOFT
: return DevFmtUShort
;
801 case ALC_INT_SOFT
: return DevFmtInt
;
802 case ALC_UNSIGNED_INT_SOFT
: return DevFmtUInt
;
803 case ALC_FLOAT_SOFT
: return DevFmtFloat
;
805 WARN("Unsupported format type: 0x%04x\n", type
);
808 ALCenum
EnumFromDevFmt(DevFmtType type
)
812 case DevFmtByte
: return ALC_BYTE_SOFT
;
813 case DevFmtUByte
: return ALC_UNSIGNED_BYTE_SOFT
;
814 case DevFmtShort
: return ALC_SHORT_SOFT
;
815 case DevFmtUShort
: return ALC_UNSIGNED_SHORT_SOFT
;
816 case DevFmtInt
: return ALC_INT_SOFT
;
817 case DevFmtUInt
: return ALC_UNSIGNED_INT_SOFT
;
818 case DevFmtFloat
: return ALC_FLOAT_SOFT
;
820 throw std::runtime_error
{"Invalid DevFmtType: "+std::to_string(int(type
))};
823 std::optional
<DevFmtChannels
> DevFmtChannelsFromEnum(ALCenum channels
)
827 case ALC_MONO_SOFT
: return DevFmtMono
;
828 case ALC_STEREO_SOFT
: return DevFmtStereo
;
829 case ALC_QUAD_SOFT
: return DevFmtQuad
;
830 case ALC_5POINT1_SOFT
: return DevFmtX51
;
831 case ALC_6POINT1_SOFT
: return DevFmtX61
;
832 case ALC_7POINT1_SOFT
: return DevFmtX71
;
833 case ALC_BFORMAT3D_SOFT
: return DevFmtAmbi3D
;
835 WARN("Unsupported format channels: 0x%04x\n", channels
);
838 ALCenum
EnumFromDevFmt(DevFmtChannels channels
)
842 case DevFmtMono
: return ALC_MONO_SOFT
;
843 case DevFmtStereo
: return ALC_STEREO_SOFT
;
844 case DevFmtQuad
: return ALC_QUAD_SOFT
;
845 case DevFmtX51
: return ALC_5POINT1_SOFT
;
846 case DevFmtX61
: return ALC_6POINT1_SOFT
;
847 case DevFmtX71
: return ALC_7POINT1_SOFT
;
848 case DevFmtAmbi3D
: return ALC_BFORMAT3D_SOFT
;
849 /* FIXME: Shouldn't happen. */
851 case DevFmtX3D71
: break;
853 throw std::runtime_error
{"Invalid DevFmtChannels: "+std::to_string(int(channels
))};
856 std::optional
<DevAmbiLayout
> DevAmbiLayoutFromEnum(ALCenum layout
)
860 case ALC_FUMA_SOFT
: return DevAmbiLayout::FuMa
;
861 case ALC_ACN_SOFT
: return DevAmbiLayout::ACN
;
863 WARN("Unsupported ambisonic layout: 0x%04x\n", layout
);
866 ALCenum
EnumFromDevAmbi(DevAmbiLayout layout
)
870 case DevAmbiLayout::FuMa
: return ALC_FUMA_SOFT
;
871 case DevAmbiLayout::ACN
: return ALC_ACN_SOFT
;
873 throw std::runtime_error
{"Invalid DevAmbiLayout: "+std::to_string(int(layout
))};
876 std::optional
<DevAmbiScaling
> DevAmbiScalingFromEnum(ALCenum scaling
)
880 case ALC_FUMA_SOFT
: return DevAmbiScaling::FuMa
;
881 case ALC_SN3D_SOFT
: return DevAmbiScaling::SN3D
;
882 case ALC_N3D_SOFT
: return DevAmbiScaling::N3D
;
884 WARN("Unsupported ambisonic scaling: 0x%04x\n", scaling
);
887 ALCenum
EnumFromDevAmbi(DevAmbiScaling scaling
)
891 case DevAmbiScaling::FuMa
: return ALC_FUMA_SOFT
;
892 case DevAmbiScaling::SN3D
: return ALC_SN3D_SOFT
;
893 case DevAmbiScaling::N3D
: return ALC_N3D_SOFT
;
895 throw std::runtime_error
{"Invalid DevAmbiScaling: "+std::to_string(int(scaling
))};
899 /* Downmixing channel arrays, to map a device format's missing channels to
900 * existing ones. Based on what PipeWire does, though simplified.
902 constexpr float inv_sqrt2f
{static_cast<float>(1.0 / al::numbers::sqrt2
)};
903 constexpr std::array FrontStereo3dB
{
904 InputRemixMap::TargetMix
{FrontLeft
, inv_sqrt2f
},
905 InputRemixMap::TargetMix
{FrontRight
, inv_sqrt2f
}
907 constexpr std::array FrontStereo6dB
{
908 InputRemixMap::TargetMix
{FrontLeft
, 0.5f
},
909 InputRemixMap::TargetMix
{FrontRight
, 0.5f
}
911 constexpr std::array SideStereo3dB
{
912 InputRemixMap::TargetMix
{SideLeft
, inv_sqrt2f
},
913 InputRemixMap::TargetMix
{SideRight
, inv_sqrt2f
}
915 constexpr std::array BackStereo3dB
{
916 InputRemixMap::TargetMix
{BackLeft
, inv_sqrt2f
},
917 InputRemixMap::TargetMix
{BackRight
, inv_sqrt2f
}
919 constexpr std::array FrontLeft3dB
{InputRemixMap::TargetMix
{FrontLeft
, inv_sqrt2f
}};
920 constexpr std::array FrontRight3dB
{InputRemixMap::TargetMix
{FrontRight
, inv_sqrt2f
}};
921 constexpr std::array SideLeft0dB
{InputRemixMap::TargetMix
{SideLeft
, 1.0f
}};
922 constexpr std::array SideRight0dB
{InputRemixMap::TargetMix
{SideRight
, 1.0f
}};
923 constexpr std::array BackLeft0dB
{InputRemixMap::TargetMix
{BackLeft
, 1.0f
}};
924 constexpr std::array BackRight0dB
{InputRemixMap::TargetMix
{BackRight
, 1.0f
}};
925 constexpr std::array BackCenter3dB
{InputRemixMap::TargetMix
{BackCenter
, inv_sqrt2f
}};
927 constexpr std::array StereoDownmix
{
928 InputRemixMap
{FrontCenter
, FrontStereo3dB
},
929 InputRemixMap
{SideLeft
, FrontLeft3dB
},
930 InputRemixMap
{SideRight
, FrontRight3dB
},
931 InputRemixMap
{BackLeft
, FrontLeft3dB
},
932 InputRemixMap
{BackRight
, FrontRight3dB
},
933 InputRemixMap
{BackCenter
, FrontStereo6dB
},
935 constexpr std::array QuadDownmix
{
936 InputRemixMap
{FrontCenter
, FrontStereo3dB
},
937 InputRemixMap
{SideLeft
, BackLeft0dB
},
938 InputRemixMap
{SideRight
, BackRight0dB
},
939 InputRemixMap
{BackCenter
, BackStereo3dB
},
941 constexpr std::array X51Downmix
{
942 InputRemixMap
{BackLeft
, SideLeft0dB
},
943 InputRemixMap
{BackRight
, SideRight0dB
},
944 InputRemixMap
{BackCenter
, SideStereo3dB
},
946 constexpr std::array X61Downmix
{
947 InputRemixMap
{BackLeft
, BackCenter3dB
},
948 InputRemixMap
{BackRight
, BackCenter3dB
},
950 constexpr std::array X71Downmix
{
951 InputRemixMap
{BackCenter
, BackStereo3dB
},
955 std::unique_ptr
<Compressor
> CreateDeviceLimiter(const ALCdevice
*device
, const float threshold
)
957 static constexpr bool AutoKnee
{true};
958 static constexpr bool AutoAttack
{true};
959 static constexpr bool AutoRelease
{true};
960 static constexpr bool AutoPostGain
{true};
961 static constexpr bool AutoDeclip
{true};
962 static constexpr float LookAheadTime
{0.001f
};
963 static constexpr float HoldTime
{0.002f
};
964 static constexpr float PreGainDb
{0.0f
};
965 static constexpr float PostGainDb
{0.0f
};
966 static constexpr float Ratio
{std::numeric_limits
<float>::infinity()};
967 static constexpr float KneeDb
{0.0f
};
968 static constexpr float AttackTime
{0.02f
};
969 static constexpr float ReleaseTime
{0.2f
};
971 return Compressor::Create(device
->RealOut
.Buffer
.size(), static_cast<float>(device
->Frequency
),
972 AutoKnee
, AutoAttack
, AutoRelease
, AutoPostGain
, AutoDeclip
, LookAheadTime
, HoldTime
,
973 PreGainDb
, PostGainDb
, threshold
, Ratio
, KneeDb
, AttackTime
, ReleaseTime
);
977 * Updates the device's base clock time with however many samples have been
978 * done. This is used so frequency changes on the device don't cause the time
979 * to jump forward or back. Must not be called while the device is running/
982 inline void UpdateClockBase(ALCdevice
*device
)
984 const auto mixLock
= device
->getWriteMixLock();
986 auto samplesDone
= device
->mSamplesDone
.load(std::memory_order_relaxed
);
987 auto clockBase
= device
->mClockBase
.load(std::memory_order_relaxed
);
989 clockBase
+= nanoseconds
{seconds
{samplesDone
}} / device
->Frequency
;
990 device
->mClockBase
.store(clockBase
, std::memory_order_relaxed
);
991 device
->mSamplesDone
.store(0, std::memory_order_relaxed
);
995 * Updates device parameters according to the attribute list (caller is
996 * responsible for holding the list lock).
998 ALCenum
UpdateDeviceParams(ALCdevice
*device
, const int *attrList
)
1000 if((!attrList
|| !attrList
[0]) && device
->Type
== DeviceType::Loopback
)
1002 WARN("Missing attributes for loopback device\n");
1003 return ALC_INVALID_VALUE
;
1006 uint numMono
{device
->NumMonoSources
};
1007 uint numStereo
{device
->NumStereoSources
};
1008 uint numSends
{device
->NumAuxSends
};
1009 std::optional
<StereoEncoding
> stereomode
;
1010 std::optional
<bool> optlimit
;
1011 std::optional
<uint
> optsrate
;
1012 std::optional
<DevFmtChannels
> optchans
;
1013 std::optional
<DevFmtType
> opttype
;
1014 std::optional
<DevAmbiLayout
> optlayout
;
1015 std::optional
<DevAmbiScaling
> optscale
;
1016 uint period_size
{DefaultUpdateSize
};
1017 uint buffer_size
{DefaultUpdateSize
* DefaultNumUpdates
};
1021 if(device
->Type
!= DeviceType::Loopback
)
1023 /* Get default settings from the user configuration */
1025 if(auto freqopt
= device
->configValue
<uint
>({}, "frequency"))
1027 optsrate
= clampu(*freqopt
, MinOutputRate
, MaxOutputRate
);
1029 const double scale
{static_cast<double>(*optsrate
) / double{DefaultOutputRate
}};
1030 period_size
= static_cast<uint
>(std::lround(period_size
* scale
));
1033 if(auto persizeopt
= device
->configValue
<uint
>({}, "period_size"))
1034 period_size
= clampu(*persizeopt
, 64, 8192);
1035 if(auto numperopt
= device
->configValue
<uint
>({}, "periods"))
1036 buffer_size
= clampu(*numperopt
, 2, 16) * period_size
;
1038 buffer_size
= period_size
* uint
{DefaultNumUpdates
};
1040 if(auto typeopt
= device
->configValue
<std::string
>({}, "sample-type"))
1043 std::string_view name
;
1046 constexpr std::array typelist
{
1047 TypeMap
{"int8"sv
, DevFmtByte
},
1048 TypeMap
{"uint8"sv
, DevFmtUByte
},
1049 TypeMap
{"int16"sv
, DevFmtShort
},
1050 TypeMap
{"uint16"sv
, DevFmtUShort
},
1051 TypeMap
{"int32"sv
, DevFmtInt
},
1052 TypeMap
{"uint32"sv
, DevFmtUInt
},
1053 TypeMap
{"float32"sv
, DevFmtFloat
},
1056 const ALCchar
*fmt
{typeopt
->c_str()};
1057 auto iter
= std::find_if(typelist
.begin(), typelist
.end(),
1058 [svfmt
=std::string_view
{fmt
}](const TypeMap
&entry
) -> bool
1059 { return al::case_compare(entry
.name
, svfmt
) == 0; });
1060 if(iter
== typelist
.end())
1061 ERR("Unsupported sample-type: %s\n", fmt
);
1063 opttype
= iter
->type
;
1065 if(auto chanopt
= device
->configValue
<std::string
>({}, "channels"))
1068 std::string_view name
;
1069 DevFmtChannels chans
;
1072 constexpr std::array chanlist
{
1073 ChannelMap
{"mono"sv
, DevFmtMono
, 0},
1074 ChannelMap
{"stereo"sv
, DevFmtStereo
, 0},
1075 ChannelMap
{"quad"sv
, DevFmtQuad
, 0},
1076 ChannelMap
{"surround51"sv
, DevFmtX51
, 0},
1077 ChannelMap
{"surround61"sv
, DevFmtX61
, 0},
1078 ChannelMap
{"surround71"sv
, DevFmtX71
, 0},
1079 ChannelMap
{"surround714"sv
, DevFmtX714
, 0},
1080 ChannelMap
{"surround3d71"sv
, DevFmtX3D71
, 0},
1081 ChannelMap
{"surround51rear"sv
, DevFmtX51
, 0},
1082 ChannelMap
{"ambi1"sv
, DevFmtAmbi3D
, 1},
1083 ChannelMap
{"ambi2"sv
, DevFmtAmbi3D
, 2},
1084 ChannelMap
{"ambi3"sv
, DevFmtAmbi3D
, 3},
1087 const ALCchar
*fmt
{chanopt
->c_str()};
1088 auto iter
= std::find_if(chanlist
.begin(), chanlist
.end(),
1089 [svfmt
=std::string_view
{fmt
}](const ChannelMap
&entry
) -> bool
1090 { return al::case_compare(entry
.name
, svfmt
) == 0; });
1091 if(iter
== chanlist
.end())
1092 ERR("Unsupported channels: %s\n", fmt
);
1095 optchans
= iter
->chans
;
1096 aorder
= iter
->order
;
1099 if(auto ambiopt
= device
->configValue
<std::string
>({}, "ambi-format"sv
))
1101 if(al::case_compare(*ambiopt
, "fuma"sv
) == 0)
1103 optlayout
= DevAmbiLayout::FuMa
;
1104 optscale
= DevAmbiScaling::FuMa
;
1106 else if(al::case_compare(*ambiopt
, "acn+fuma"sv
) == 0)
1108 optlayout
= DevAmbiLayout::ACN
;
1109 optscale
= DevAmbiScaling::FuMa
;
1111 else if(al::case_compare(*ambiopt
, "ambix"sv
) == 0
1112 || al::case_compare(*ambiopt
, "acn+sn3d"sv
) == 0)
1114 optlayout
= DevAmbiLayout::ACN
;
1115 optscale
= DevAmbiScaling::SN3D
;
1117 else if(al::case_compare(*ambiopt
, "acn+n3d"sv
) == 0)
1119 optlayout
= DevAmbiLayout::ACN
;
1120 optscale
= DevAmbiScaling::N3D
;
1123 ERR("Unsupported ambi-format: %s\n", ambiopt
->c_str());
1126 if(auto hrtfopt
= device
->configValue
<std::string
>({}, "hrtf"sv
))
1128 WARN("general/hrtf is deprecated, please use stereo-encoding instead\n");
1130 if(al::case_compare(*hrtfopt
, "true"sv
) == 0)
1131 stereomode
= StereoEncoding::Hrtf
;
1132 else if(al::case_compare(*hrtfopt
, "false"sv
) == 0)
1134 if(!stereomode
|| *stereomode
== StereoEncoding::Hrtf
)
1135 stereomode
= StereoEncoding::Default
;
1137 else if(al::case_compare(*hrtfopt
, "auto"sv
) != 0)
1138 ERR("Unexpected hrtf value: %s\n", hrtfopt
->c_str());
1142 if(auto encopt
= device
->configValue
<std::string
>({}, "stereo-encoding"sv
))
1144 if(al::case_compare(*encopt
, "basic"sv
) == 0 || al::case_compare(*encopt
, "panpot"sv
) == 0)
1145 stereomode
= StereoEncoding::Basic
;
1146 else if(al::case_compare(*encopt
, "uhj") == 0)
1147 stereomode
= StereoEncoding::Uhj
;
1148 else if(al::case_compare(*encopt
, "hrtf") == 0)
1149 stereomode
= StereoEncoding::Hrtf
;
1151 ERR("Unexpected stereo-encoding: %s\n", encopt
->c_str());
1154 // Check for app-specified attributes
1155 if(attrList
&& attrList
[0])
1157 ALenum outmode
{ALC_ANY_SOFT
};
1158 std::optional
<bool> opthrtf
;
1161 #define ATTRIBUTE(a) a: TRACE("%s = %d\n", #a, attrList[attrIdx + 1]);
1163 while(attrList
[attrIdx
])
1165 switch(attrList
[attrIdx
])
1167 case ATTRIBUTE(ALC_FORMAT_CHANNELS_SOFT
)
1168 if(device
->Type
== DeviceType::Loopback
)
1169 optchans
= DevFmtChannelsFromEnum(attrList
[attrIdx
+ 1]);
1172 case ATTRIBUTE(ALC_FORMAT_TYPE_SOFT
)
1173 if(device
->Type
== DeviceType::Loopback
)
1174 opttype
= DevFmtTypeFromEnum(attrList
[attrIdx
+ 1]);
1177 case ATTRIBUTE(ALC_FREQUENCY
)
1178 freqAttr
= attrList
[attrIdx
+ 1];
1181 case ATTRIBUTE(ALC_AMBISONIC_LAYOUT_SOFT
)
1182 if(device
->Type
== DeviceType::Loopback
)
1183 optlayout
= DevAmbiLayoutFromEnum(attrList
[attrIdx
+ 1]);
1186 case ATTRIBUTE(ALC_AMBISONIC_SCALING_SOFT
)
1187 if(device
->Type
== DeviceType::Loopback
)
1188 optscale
= DevAmbiScalingFromEnum(attrList
[attrIdx
+ 1]);
1191 case ATTRIBUTE(ALC_AMBISONIC_ORDER_SOFT
)
1192 if(device
->Type
== DeviceType::Loopback
)
1193 aorder
= static_cast<uint
>(attrList
[attrIdx
+ 1]);
1196 case ATTRIBUTE(ALC_MONO_SOURCES
)
1197 numMono
= static_cast<uint
>(attrList
[attrIdx
+ 1]);
1198 if(numMono
> INT_MAX
) numMono
= 0;
1201 case ATTRIBUTE(ALC_STEREO_SOURCES
)
1202 numStereo
= static_cast<uint
>(attrList
[attrIdx
+ 1]);
1203 if(numStereo
> INT_MAX
) numStereo
= 0;
1206 case ATTRIBUTE(ALC_MAX_AUXILIARY_SENDS
)
1207 numSends
= static_cast<uint
>(attrList
[attrIdx
+ 1]);
1208 if(numSends
> INT_MAX
) numSends
= 0;
1209 else numSends
= minu(numSends
, MaxSendCount
);
1212 case ATTRIBUTE(ALC_HRTF_SOFT
)
1213 if(attrList
[attrIdx
+ 1] == ALC_FALSE
)
1215 else if(attrList
[attrIdx
+ 1] == ALC_TRUE
)
1217 else if(attrList
[attrIdx
+ 1] == ALC_DONT_CARE_SOFT
)
1218 opthrtf
= std::nullopt
;
1221 case ATTRIBUTE(ALC_HRTF_ID_SOFT
)
1222 hrtf_id
= attrList
[attrIdx
+ 1];
1225 case ATTRIBUTE(ALC_OUTPUT_LIMITER_SOFT
)
1226 if(attrList
[attrIdx
+ 1] == ALC_FALSE
)
1228 else if(attrList
[attrIdx
+ 1] == ALC_TRUE
)
1230 else if(attrList
[attrIdx
+ 1] == ALC_DONT_CARE_SOFT
)
1231 optlimit
= std::nullopt
;
1234 case ATTRIBUTE(ALC_OUTPUT_MODE_SOFT
)
1235 outmode
= attrList
[attrIdx
+ 1];
1239 TRACE("0x%04X = %d (0x%x)\n", attrList
[attrIdx
],
1240 attrList
[attrIdx
+ 1], attrList
[attrIdx
+ 1]);
1248 if(device
->Type
== DeviceType::Loopback
)
1250 if(!optchans
|| !opttype
)
1251 return ALC_INVALID_VALUE
;
1252 if(freqAttr
< int{MinOutputRate
} || freqAttr
> int{MaxOutputRate
})
1253 return ALC_INVALID_VALUE
;
1254 if(*optchans
== DevFmtAmbi3D
)
1256 if(!optlayout
|| !optscale
)
1257 return ALC_INVALID_VALUE
;
1258 if(aorder
< 1 || aorder
> MaxAmbiOrder
)
1259 return ALC_INVALID_VALUE
;
1260 if((*optlayout
== DevAmbiLayout::FuMa
|| *optscale
== DevAmbiScaling::FuMa
)
1262 return ALC_INVALID_VALUE
;
1264 else if(*optchans
== DevFmtStereo
)
1269 stereomode
= StereoEncoding::Hrtf
;
1272 if(stereomode
.value_or(StereoEncoding::Hrtf
) == StereoEncoding::Hrtf
)
1273 stereomode
= StereoEncoding::Default
;
1277 if(outmode
== ALC_STEREO_BASIC_SOFT
)
1278 stereomode
= StereoEncoding::Basic
;
1279 else if(outmode
== ALC_STEREO_UHJ_SOFT
)
1280 stereomode
= StereoEncoding::Uhj
;
1281 else if(outmode
== ALC_STEREO_HRTF_SOFT
)
1282 stereomode
= StereoEncoding::Hrtf
;
1285 optsrate
= static_cast<uint
>(freqAttr
);
1292 stereomode
= StereoEncoding::Hrtf
;
1295 if(stereomode
.value_or(StereoEncoding::Hrtf
) == StereoEncoding::Hrtf
)
1296 stereomode
= StereoEncoding::Default
;
1300 if(outmode
!= ALC_ANY_SOFT
)
1302 using OutputMode
= ALCdevice::OutputMode
;
1303 switch(OutputMode(outmode
))
1305 case OutputMode::Any
: break;
1306 case OutputMode::Mono
: optchans
= DevFmtMono
; break;
1307 case OutputMode::Stereo
: optchans
= DevFmtStereo
; break;
1308 case OutputMode::StereoBasic
:
1309 optchans
= DevFmtStereo
;
1310 stereomode
= StereoEncoding::Basic
;
1312 case OutputMode::Uhj2
:
1313 optchans
= DevFmtStereo
;
1314 stereomode
= StereoEncoding::Uhj
;
1316 case OutputMode::Hrtf
:
1317 optchans
= DevFmtStereo
;
1318 stereomode
= StereoEncoding::Hrtf
;
1320 case OutputMode::Quad
: optchans
= DevFmtQuad
; break;
1321 case OutputMode::X51
: optchans
= DevFmtX51
; break;
1322 case OutputMode::X61
: optchans
= DevFmtX61
; break;
1323 case OutputMode::X71
: optchans
= DevFmtX71
; break;
1329 uint oldrate
= optsrate
.value_or(DefaultOutputRate
);
1330 freqAttr
= clampi(freqAttr
, MinOutputRate
, MaxOutputRate
);
1332 const double scale
{static_cast<double>(freqAttr
) / oldrate
};
1333 period_size
= static_cast<uint
>(std::lround(period_size
* scale
));
1334 buffer_size
= static_cast<uint
>(std::lround(buffer_size
* scale
));
1335 optsrate
= static_cast<uint
>(freqAttr
);
1339 /* If a context is already running on the device, stop playback so the
1340 * device attributes can be updated.
1342 if(device
->mDeviceState
== DeviceState::Playing
)
1344 device
->Backend
->stop();
1345 device
->mDeviceState
= DeviceState::Unprepared
;
1348 UpdateClockBase(device
);
1351 if(device
->mDeviceState
== DeviceState::Playing
)
1352 return ALC_NO_ERROR
;
1354 device
->mDeviceState
= DeviceState::Unprepared
;
1355 device
->AvgSpeakerDist
= 0.0f
;
1356 device
->mNFCtrlFilter
= NfcFilter
{};
1357 device
->mUhjEncoder
= nullptr;
1358 device
->AmbiDecoder
= nullptr;
1359 device
->Bs2b
= nullptr;
1360 device
->PostProcess
= nullptr;
1362 device
->Limiter
= nullptr;
1363 device
->ChannelDelays
= nullptr;
1365 std::fill(std::begin(device
->HrtfAccumData
), std::end(device
->HrtfAccumData
), float2
{});
1367 device
->Dry
.AmbiMap
.fill(BFChannelConfig
{});
1368 device
->Dry
.Buffer
= {};
1369 std::fill(std::begin(device
->NumChannelsPerOrder
), std::end(device
->NumChannelsPerOrder
), 0u);
1370 device
->RealOut
.RemixMap
= {};
1371 device
->RealOut
.ChannelIndex
.fill(InvalidChannelIndex
);
1372 device
->RealOut
.Buffer
= {};
1373 device
->MixBuffer
.clear();
1374 device
->MixBuffer
.shrink_to_fit();
1376 UpdateClockBase(device
);
1377 device
->FixedLatency
= nanoseconds::zero();
1379 device
->DitherDepth
= 0.0f
;
1380 device
->DitherSeed
= DitherRNGSeed
;
1382 device
->mHrtfStatus
= ALC_HRTF_DISABLED_SOFT
;
1384 /*************************************************************************
1385 * Update device format request
1388 if(device
->Type
== DeviceType::Loopback
)
1390 device
->Frequency
= *optsrate
;
1391 device
->FmtChans
= *optchans
;
1392 device
->FmtType
= *opttype
;
1393 if(device
->FmtChans
== DevFmtAmbi3D
)
1395 device
->mAmbiOrder
= aorder
;
1396 device
->mAmbiLayout
= *optlayout
;
1397 device
->mAmbiScale
= *optscale
;
1399 device
->Flags
.set(FrequencyRequest
).set(ChannelsRequest
).set(SampleTypeRequest
);
1403 device
->FmtType
= opttype
.value_or(DevFmtTypeDefault
);
1404 device
->FmtChans
= optchans
.value_or(DevFmtChannelsDefault
);
1405 device
->mAmbiOrder
= 0;
1406 device
->BufferSize
= buffer_size
;
1407 device
->UpdateSize
= period_size
;
1408 device
->Frequency
= optsrate
.value_or(DefaultOutputRate
);
1409 device
->Flags
.set(FrequencyRequest
, optsrate
.has_value())
1410 .set(ChannelsRequest
, optchans
.has_value())
1411 .set(SampleTypeRequest
, opttype
.has_value());
1413 if(device
->FmtChans
== DevFmtAmbi3D
)
1415 device
->mAmbiOrder
= clampu(aorder
, 1, MaxAmbiOrder
);
1416 device
->mAmbiLayout
= optlayout
.value_or(DevAmbiLayout::Default
);
1417 device
->mAmbiScale
= optscale
.value_or(DevAmbiScaling::Default
);
1418 if(device
->mAmbiOrder
> 3
1419 && (device
->mAmbiLayout
== DevAmbiLayout::FuMa
1420 || device
->mAmbiScale
== DevAmbiScaling::FuMa
))
1422 ERR("FuMa is incompatible with %d%s order ambisonics (up to 3rd order only)\n",
1423 device
->mAmbiOrder
, GetCounterSuffix(device
->mAmbiOrder
));
1424 device
->mAmbiOrder
= 3;
1429 TRACE("Pre-reset: %s%s, %s%s, %s%uhz, %u / %u buffer\n",
1430 device
->Flags
.test(ChannelsRequest
)?"*":"", DevFmtChannelsString(device
->FmtChans
),
1431 device
->Flags
.test(SampleTypeRequest
)?"*":"", DevFmtTypeString(device
->FmtType
),
1432 device
->Flags
.test(FrequencyRequest
)?"*":"", device
->Frequency
,
1433 device
->UpdateSize
, device
->BufferSize
);
1435 const uint oldFreq
{device
->Frequency
};
1436 const DevFmtChannels oldChans
{device
->FmtChans
};
1437 const DevFmtType oldType
{device
->FmtType
};
1439 auto backend
= device
->Backend
.get();
1440 if(!backend
->reset())
1441 throw al::backend_exception
{al::backend_error::DeviceError
, "Device reset failure"};
1443 catch(std::exception
&e
) {
1444 ERR("Device error: %s\n", e
.what());
1445 device
->handleDisconnect("%s", e
.what());
1446 return ALC_INVALID_DEVICE
;
1449 if(device
->FmtChans
!= oldChans
&& device
->Flags
.test(ChannelsRequest
))
1451 ERR("Failed to set %s, got %s instead\n", DevFmtChannelsString(oldChans
),
1452 DevFmtChannelsString(device
->FmtChans
));
1453 device
->Flags
.reset(ChannelsRequest
);
1455 if(device
->FmtType
!= oldType
&& device
->Flags
.test(SampleTypeRequest
))
1457 ERR("Failed to set %s, got %s instead\n", DevFmtTypeString(oldType
),
1458 DevFmtTypeString(device
->FmtType
));
1459 device
->Flags
.reset(SampleTypeRequest
);
1461 if(device
->Frequency
!= oldFreq
&& device
->Flags
.test(FrequencyRequest
))
1463 WARN("Failed to set %uhz, got %uhz instead\n", oldFreq
, device
->Frequency
);
1464 device
->Flags
.reset(FrequencyRequest
);
1467 TRACE("Post-reset: %s, %s, %uhz, %u / %u buffer\n",
1468 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1469 device
->Frequency
, device
->UpdateSize
, device
->BufferSize
);
1471 if(device
->Type
!= DeviceType::Loopback
)
1473 if(auto modeopt
= device
->configValue
<std::string
>({}, "stereo-mode"))
1475 if(al::case_compare(*modeopt
, "headphones"sv
) == 0)
1476 device
->Flags
.set(DirectEar
);
1477 else if(al::case_compare(*modeopt
, "speakers"sv
) == 0)
1478 device
->Flags
.reset(DirectEar
);
1479 else if(al::case_compare(*modeopt
, "auto"sv
) != 0)
1480 ERR("Unexpected stereo-mode: %s\n", modeopt
->c_str());
1484 aluInitRenderer(device
, hrtf_id
, stereomode
);
1486 /* Calculate the max number of sources, and split them between the mono and
1487 * stereo count given the requested number of stereo sources.
1489 if(auto srcsopt
= device
->configValue
<uint
>({}, "sources"sv
))
1491 if(*srcsopt
<= 0) numMono
= 256;
1492 else numMono
= maxu(*srcsopt
, 16);
1496 numMono
= minu(numMono
, INT_MAX
-numStereo
);
1497 numMono
= maxu(numMono
+numStereo
, 256);
1499 numStereo
= minu(numStereo
, numMono
);
1500 numMono
-= numStereo
;
1501 device
->SourcesMax
= numMono
+ numStereo
;
1502 device
->NumMonoSources
= numMono
;
1503 device
->NumStereoSources
= numStereo
;
1505 if(auto sendsopt
= device
->configValue
<int>({}, "sends"sv
))
1506 numSends
= minu(numSends
, static_cast<uint
>(clampi(*sendsopt
, 0, MaxSendCount
)));
1507 device
->NumAuxSends
= numSends
;
1509 TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n",
1510 device
->SourcesMax
, device
->NumMonoSources
, device
->NumStereoSources
,
1511 device
->AuxiliaryEffectSlotMax
, device
->NumAuxSends
);
1513 switch(device
->FmtChans
)
1515 case DevFmtMono
: break;
1517 if(!device
->mUhjEncoder
)
1518 device
->RealOut
.RemixMap
= StereoDownmix
;
1520 case DevFmtQuad
: device
->RealOut
.RemixMap
= QuadDownmix
; break;
1521 case DevFmtX51
: device
->RealOut
.RemixMap
= X51Downmix
; break;
1522 case DevFmtX61
: device
->RealOut
.RemixMap
= X61Downmix
; break;
1523 case DevFmtX71
: device
->RealOut
.RemixMap
= X71Downmix
; break;
1524 case DevFmtX714
: device
->RealOut
.RemixMap
= X71Downmix
; break;
1525 case DevFmtX3D71
: device
->RealOut
.RemixMap
= X51Downmix
; break;
1526 case DevFmtAmbi3D
: break;
1529 size_t sample_delay
{0};
1530 if(auto *encoder
{device
->mUhjEncoder
.get()})
1531 sample_delay
+= encoder
->getDelay();
1533 if(device
->getConfigValueBool({}, "dither"sv
, true))
1535 int depth
{device
->configValue
<int>({}, "dither-depth"sv
).value_or(0)};
1538 switch(device
->FmtType
)
1557 depth
= clampi(depth
, 2, 24);
1558 device
->DitherDepth
= std::pow(2.0f
, static_cast<float>(depth
-1));
1561 if(!(device
->DitherDepth
> 0.0f
))
1562 TRACE("Dithering disabled\n");
1564 TRACE("Dithering enabled (%d-bit, %g)\n", float2int(std::log2(device
->DitherDepth
)+0.5f
)+1,
1565 device
->DitherDepth
);
1568 optlimit
= device
->configValue
<bool>({}, "output-limiter");
1570 /* If the gain limiter is unset, use the limiter for integer-based output
1571 * (where samples must be clamped), and don't for floating-point (which can
1572 * take unclamped samples).
1576 switch(device
->FmtType
)
1590 if(optlimit
.value_or(false) == false)
1591 TRACE("Output limiter disabled\n");
1594 float thrshld
{1.0f
};
1595 switch(device
->FmtType
)
1599 thrshld
= 127.0f
/ 128.0f
;
1603 thrshld
= 32767.0f
/ 32768.0f
;
1610 if(device
->DitherDepth
> 0.0f
)
1611 thrshld
-= 1.0f
/ device
->DitherDepth
;
1613 const float thrshld_dB
{std::log10(thrshld
) * 20.0f
};
1614 auto limiter
= CreateDeviceLimiter(device
, thrshld_dB
);
1616 sample_delay
+= limiter
->getLookAhead();
1617 device
->Limiter
= std::move(limiter
);
1618 TRACE("Output limiter enabled, %.4fdB limit\n", thrshld_dB
);
1621 /* Convert the sample delay from samples to nanosamples to nanoseconds. */
1622 sample_delay
= std::min
<size_t>(sample_delay
, std::numeric_limits
<int>::max());
1623 device
->FixedLatency
+= nanoseconds
{seconds
{sample_delay
}} / device
->Frequency
;
1624 TRACE("Fixed device latency: %" PRId64
"ns\n", int64_t{device
->FixedLatency
.count()});
1626 FPUCtl mixer_mode
{};
1627 for(ContextBase
*ctxbase
: *device
->mContexts
.load())
1629 auto *context
= static_cast<ALCcontext
*>(ctxbase
);
1631 std::unique_lock
<std::mutex
> proplock
{context
->mPropLock
};
1632 std::unique_lock
<std::mutex
> slotlock
{context
->mEffectSlotLock
};
1634 /* Clear out unused effect slot clusters. */
1635 auto slot_cluster_not_in_use
= [](ContextBase::EffectSlotCluster
&clusterptr
)
1637 const auto cluster
= al::span
{*clusterptr
};
1638 for(size_t i
{0};i
< cluster
.size();++i
)
1640 if(cluster
[i
].InUse
)
1645 auto slotcluster_iter
= std::remove_if(context
->mEffectSlotClusters
.begin(),
1646 context
->mEffectSlotClusters
.end(), slot_cluster_not_in_use
);
1647 context
->mEffectSlotClusters
.erase(slotcluster_iter
, context
->mEffectSlotClusters
.end());
1649 /* Free all wet buffers. Any in use will be reallocated with an updated
1650 * configuration in aluInitEffectPanning.
1652 for(auto& clusterptr
: context
->mEffectSlotClusters
)
1654 const auto cluster
= al::span
{*clusterptr
};
1655 for(size_t i
{0};i
< cluster
.size();++i
)
1657 cluster
[i
].mWetBuffer
.clear();
1658 cluster
[i
].mWetBuffer
.shrink_to_fit();
1659 cluster
[i
].Wet
.Buffer
= {};
1663 if(ALeffectslot
*slot
{context
->mDefaultSlot
.get()})
1665 auto *slotbase
= slot
->mSlot
;
1666 aluInitEffectPanning(slotbase
, context
);
1668 if(auto *props
= slotbase
->Update
.exchange(nullptr, std::memory_order_relaxed
))
1669 AtomicReplaceHead(context
->mFreeEffectSlotProps
, props
);
1671 EffectState
*state
{slot
->Effect
.State
.get()};
1672 state
->mOutTarget
= device
->Dry
.Buffer
;
1673 state
->deviceUpdate(device
, slot
->Buffer
);
1674 slot
->mPropsDirty
= true;
1677 if(EffectSlotArray
*curarray
{context
->mActiveAuxSlots
.load(std::memory_order_relaxed
)})
1678 std::fill_n(curarray
->end(), curarray
->size(), nullptr);
1679 for(auto &sublist
: context
->mEffectSlotList
)
1681 uint64_t usemask
{~sublist
.FreeMask
};
1684 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
1685 auto &slot
= (*sublist
.EffectSlots
)[idx
];
1686 usemask
&= ~(1_u64
<< idx
);
1688 auto *slotbase
= slot
.mSlot
;
1689 aluInitEffectPanning(slotbase
, context
);
1691 if(auto *props
= slotbase
->Update
.exchange(nullptr, std::memory_order_relaxed
))
1692 AtomicReplaceHead(context
->mFreeEffectSlotProps
, props
);
1694 EffectState
*state
{slot
.Effect
.State
.get()};
1695 state
->mOutTarget
= device
->Dry
.Buffer
;
1696 state
->deviceUpdate(device
, slot
.Buffer
);
1697 slot
.mPropsDirty
= true;
1700 /* Clear all effect slot props to let them get allocated again. */
1701 context
->mEffectSlotPropClusters
.clear();
1702 context
->mFreeEffectSlotProps
.store(nullptr, std::memory_order_relaxed
);
1705 const uint num_sends
{device
->NumAuxSends
};
1706 std::unique_lock
<std::mutex
> srclock
{context
->mSourceLock
};
1707 for(auto &sublist
: context
->mSourceList
)
1709 uint64_t usemask
{~sublist
.FreeMask
};
1712 const auto idx
= static_cast<uint
>(al::countr_zero(usemask
));
1713 auto &source
= (*sublist
.Sources
)[idx
];
1714 usemask
&= ~(1_u64
<< idx
);
1716 auto clear_send
= [](ALsource::SendData
&send
) -> void
1719 DecrementRef(send
.Slot
->ref
);
1720 send
.Slot
= nullptr;
1723 send
.HFReference
= LowPassFreqRef
;
1725 send
.LFReference
= HighPassFreqRef
;
1727 auto send_begin
= source
.Send
.begin() + static_cast<ptrdiff_t>(num_sends
);
1728 std::for_each(send_begin
, source
.Send
.end(), clear_send
);
1730 source
.mPropsDirty
= true;
1734 for(Voice
*voice
: context
->getVoicesSpan())
1736 /* Clear extraneous property set sends. */
1737 std::fill(std::begin(voice
->mProps
.Send
)+num_sends
, std::end(voice
->mProps
.Send
),
1738 VoiceProps::SendData
{});
1740 std::fill(voice
->mSend
.begin()+num_sends
, voice
->mSend
.end(), Voice::TargetData
{});
1741 for(auto &chandata
: voice
->mChans
)
1743 std::fill(chandata
.mWetParams
.begin()+num_sends
, chandata
.mWetParams
.end(),
1747 if(VoicePropsItem
*props
{voice
->mUpdate
.exchange(nullptr, std::memory_order_relaxed
)})
1748 AtomicReplaceHead(context
->mFreeVoiceProps
, props
);
1750 /* Force the voice to stopped if it was stopping. */
1751 Voice::State vstate
{Voice::Stopping
};
1752 voice
->mPlayState
.compare_exchange_strong(vstate
, Voice::Stopped
,
1753 std::memory_order_acquire
, std::memory_order_acquire
);
1754 if(voice
->mSourceID
.load(std::memory_order_relaxed
) == 0u)
1757 voice
->prepare(device
);
1759 /* Clear all voice props to let them get allocated again. */
1760 context
->mVoicePropClusters
.clear();
1761 context
->mFreeVoiceProps
.store(nullptr, std::memory_order_relaxed
);
1764 context
->mPropsDirty
= false;
1765 UpdateContextProps(context
);
1766 UpdateAllEffectSlotProps(context
);
1767 UpdateAllSourceProps(context
);
1771 device
->mDeviceState
= DeviceState::Configured
;
1772 if(!device
->Flags
.test(DevicePaused
))
1775 auto backend
= device
->Backend
.get();
1777 device
->mDeviceState
= DeviceState::Playing
;
1779 catch(al::backend_exception
& e
) {
1780 ERR("%s\n", e
.what());
1781 device
->handleDisconnect("%s", e
.what());
1782 return ALC_INVALID_DEVICE
;
1784 TRACE("Post-start: %s, %s, %uhz, %u / %u buffer\n",
1785 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1786 device
->Frequency
, device
->UpdateSize
, device
->BufferSize
);
1789 return ALC_NO_ERROR
;
1793 * Updates device parameters as above, and also first clears the disconnected
1796 bool ResetDeviceParams(ALCdevice
*device
, const int *attrList
)
1798 /* If the device was disconnected, reset it since we're opened anew. */
1799 if(!device
->Connected
.load(std::memory_order_relaxed
)) UNLIKELY
1801 /* Make sure disconnection is finished before continuing on. */
1802 std::ignore
= device
->waitForMix();
1804 for(ContextBase
*ctxbase
: *device
->mContexts
.load(std::memory_order_acquire
))
1806 auto *ctx
= static_cast<ALCcontext
*>(ctxbase
);
1807 if(!ctx
->mStopVoicesOnDisconnect
.load(std::memory_order_acquire
))
1810 /* Clear any pending voice changes and reallocate voices to get a
1813 std::lock_guard
<std::mutex
> sourcelock
{ctx
->mSourceLock
};
1814 auto *vchg
= ctx
->mCurrentVoiceChange
.load(std::memory_order_acquire
);
1815 while(auto *next
= vchg
->mNext
.load(std::memory_order_acquire
))
1817 ctx
->mCurrentVoiceChange
.store(vchg
, std::memory_order_release
);
1819 ctx
->mVoicePropClusters
.clear();
1820 ctx
->mFreeVoiceProps
.store(nullptr, std::memory_order_relaxed
);
1822 ctx
->mVoiceClusters
.clear();
1823 ctx
->allocVoices(std::max
<size_t>(256,
1824 ctx
->mActiveVoiceCount
.load(std::memory_order_relaxed
)));
1827 device
->Connected
.store(true);
1830 ALCenum err
{UpdateDeviceParams(device
, attrList
)};
1831 if(err
== ALC_NO_ERROR
) LIKELY
return ALC_TRUE
;
1833 alcSetError(device
, err
);
1838 /** Checks if the device handle is valid, and returns a new reference if so. */
1839 DeviceRef
VerifyDevice(ALCdevice
*device
)
1841 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
1842 auto iter
= std::lower_bound(DeviceList
.begin(), DeviceList
.end(), device
);
1843 if(iter
!= DeviceList
.end() && *iter
== device
)
1846 return DeviceRef
{*iter
};
1853 * Checks if the given context is valid, returning a new reference to it if so.
1855 ContextRef
VerifyContext(ALCcontext
*context
)
1857 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
1858 auto iter
= std::lower_bound(ContextList
.begin(), ContextList
.end(), context
);
1859 if(iter
!= ContextList
.end() && *iter
== context
)
1862 return ContextRef
{*iter
};
1869 FORCE_ALIGN
void ALC_APIENTRY
alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback
, void *userptr
) noexcept
1871 al_set_log_callback(callback
, userptr
);
1874 /** Returns a new reference to the currently active context for this thread. */
1875 ContextRef
GetContextRef()
1877 ALCcontext
*context
{ALCcontext::getThreadContext()};
1882 while(ALCcontext::sGlobalContextLock
.exchange(true, std::memory_order_acquire
)) {
1883 /* Wait to make sure another thread isn't trying to change the
1884 * current context and bring its refcount to 0.
1887 context
= ALCcontext::sGlobalContext
.load(std::memory_order_acquire
);
1888 if(context
) LIKELY context
->add_ref();
1889 ALCcontext::sGlobalContextLock
.store(false, std::memory_order_release
);
1891 return ContextRef
{context
};
1894 void alcSetError(ALCdevice
*device
, ALCenum errorCode
)
1896 WARN("Error generated on device %p, code 0x%04x\n", voidp
{device
}, errorCode
);
1900 /* DebugBreak() will cause an exception if there is no debugger */
1901 if(IsDebuggerPresent())
1903 #elif defined(SIGTRAP)
1909 device
->LastError
.store(errorCode
);
1911 LastNullDeviceError
.store(errorCode
);
1914 /************************************************
1915 * Standard ALC functions
1916 ************************************************/
1918 ALC_API ALCenum ALC_APIENTRY
alcGetError(ALCdevice
*device
) noexcept
1920 DeviceRef dev
{VerifyDevice(device
)};
1921 if(dev
) return dev
->LastError
.exchange(ALC_NO_ERROR
);
1922 return LastNullDeviceError
.exchange(ALC_NO_ERROR
);
1926 ALC_API
void ALC_APIENTRY
alcSuspendContext(ALCcontext
*context
) noexcept
1928 ContextRef ctx
{VerifyContext(context
)};
1931 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
1935 if(context
->mContextFlags
.test(ContextFlags::DebugBit
)) UNLIKELY
1936 ctx
->debugMessage(DebugSource::API
, DebugType::Portability
, 0, DebugSeverity::Medium
,
1937 "alcSuspendContext behavior is not portable -- some implementations suspend all "
1938 "rendering, some only defer property changes, and some are completely no-op; consider "
1939 "using alcDevicePauseSOFT to suspend all rendering, or alDeferUpdatesSOFT to only "
1940 "defer property changes");
1944 std::lock_guard
<std::mutex
> proplock
{ctx
->mPropLock
};
1945 ctx
->deferUpdates();
1949 ALC_API
void ALC_APIENTRY
alcProcessContext(ALCcontext
*context
) noexcept
1951 ContextRef ctx
{VerifyContext(context
)};
1954 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
1958 if(context
->mContextFlags
.test(ContextFlags::DebugBit
)) UNLIKELY
1959 ctx
->debugMessage(DebugSource::API
, DebugType::Portability
, 0, DebugSeverity::Medium
,
1960 "alcProcessContext behavior is not portable -- some implementations resume rendering, "
1961 "some apply deferred property changes, and some are completely no-op; consider using "
1962 "alcDeviceResumeSOFT to resume rendering, or alProcessUpdatesSOFT to apply deferred "
1963 "property changes");
1967 std::lock_guard
<std::mutex
> proplock
{ctx
->mPropLock
};
1968 ctx
->processUpdates();
1973 ALC_API
const ALCchar
* ALC_APIENTRY
alcGetString(ALCdevice
*Device
, ALCenum param
) noexcept
1975 const ALCchar
*value
{nullptr};
1979 case ALC_NO_ERROR
: value
= GetNoErrorString(); break;
1980 case ALC_INVALID_ENUM
: value
= GetInvalidEnumString(); break;
1981 case ALC_INVALID_VALUE
: value
= GetInvalidValueString(); break;
1982 case ALC_INVALID_DEVICE
: value
= GetInvalidDeviceString(); break;
1983 case ALC_INVALID_CONTEXT
: value
= GetInvalidContextString(); break;
1984 case ALC_OUT_OF_MEMORY
: value
= GetOutOfMemoryString(); break;
1986 case ALC_DEVICE_SPECIFIER
:
1987 value
= GetDefaultName();
1990 case ALC_ALL_DEVICES_SPECIFIER
:
1991 if(DeviceRef dev
{VerifyDevice(Device
)})
1993 if(dev
->Type
== DeviceType::Capture
)
1994 alcSetError(dev
.get(), ALC_INVALID_ENUM
);
1995 else if(dev
->Type
== DeviceType::Loopback
)
1996 value
= GetDefaultName();
1999 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
2000 value
= dev
->DeviceName
.c_str();
2005 ProbeAllDevicesList();
2006 value
= alcAllDevicesList
.c_str();
2010 case ALC_CAPTURE_DEVICE_SPECIFIER
:
2011 if(DeviceRef dev
{VerifyDevice(Device
)})
2013 if(dev
->Type
!= DeviceType::Capture
)
2014 alcSetError(dev
.get(), ALC_INVALID_ENUM
);
2017 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
2018 value
= dev
->DeviceName
.c_str();
2023 ProbeCaptureDeviceList();
2024 value
= alcCaptureDeviceList
.c_str();
2028 /* Default devices are always first in the list */
2029 case ALC_DEFAULT_DEVICE_SPECIFIER
:
2030 value
= GetDefaultName();
2033 case ALC_DEFAULT_ALL_DEVICES_SPECIFIER
:
2034 if(alcAllDevicesList
.empty())
2035 ProbeAllDevicesList();
2037 /* Copy first entry as default. */
2038 alcDefaultAllDevicesSpecifier
= alcAllDevicesList
.substr(0, alcAllDevicesList
.find('\0'));
2039 value
= alcDefaultAllDevicesSpecifier
.c_str();
2042 case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER
:
2043 if(alcCaptureDeviceList
.empty())
2044 ProbeCaptureDeviceList();
2046 /* Copy first entry as default. */
2047 alcCaptureDefaultDeviceSpecifier
= alcCaptureDeviceList
.substr(0,
2048 alcCaptureDeviceList
.find('\0'));
2049 value
= alcCaptureDefaultDeviceSpecifier
.c_str();
2052 case ALC_EXTENSIONS
:
2053 if(VerifyDevice(Device
))
2054 value
= GetExtensionList().data();
2056 value
= GetNoDeviceExtList().data();
2059 case ALC_HRTF_SPECIFIER_SOFT
:
2060 if(DeviceRef dev
{VerifyDevice(Device
)})
2062 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
2063 value
= (dev
->mHrtf
? dev
->mHrtfName
.c_str() : "");
2066 alcSetError(nullptr, ALC_INVALID_DEVICE
);
2070 alcSetError(VerifyDevice(Device
).get(), ALC_INVALID_ENUM
);
2078 static size_t GetIntegerv(ALCdevice
*device
, ALCenum param
, const al::span
<int> values
)
2082 alcSetError(device
, ALC_INVALID_VALUE
);
2090 case ALC_MAJOR_VERSION
:
2091 values
[0] = alcMajorVersion
;
2093 case ALC_MINOR_VERSION
:
2094 values
[0] = alcMinorVersion
;
2097 case ALC_EFX_MAJOR_VERSION
:
2098 values
[0] = alcEFXMajorVersion
;
2100 case ALC_EFX_MINOR_VERSION
:
2101 values
[0] = alcEFXMinorVersion
;
2103 case ALC_MAX_AUXILIARY_SENDS
:
2104 values
[0] = MaxSendCount
;
2107 case ALC_ATTRIBUTES_SIZE
:
2108 case ALC_ALL_ATTRIBUTES
:
2112 case ALC_MONO_SOURCES
:
2113 case ALC_STEREO_SOURCES
:
2114 case ALC_CAPTURE_SAMPLES
:
2115 case ALC_FORMAT_CHANNELS_SOFT
:
2116 case ALC_FORMAT_TYPE_SOFT
:
2117 case ALC_AMBISONIC_LAYOUT_SOFT
:
2118 case ALC_AMBISONIC_SCALING_SOFT
:
2119 case ALC_AMBISONIC_ORDER_SOFT
:
2120 case ALC_MAX_AMBISONIC_ORDER_SOFT
:
2121 alcSetError(nullptr, ALC_INVALID_DEVICE
);
2125 alcSetError(nullptr, ALC_INVALID_ENUM
);
2130 std::lock_guard
<std::mutex
> statelock
{device
->StateLock
};
2131 if(device
->Type
== DeviceType::Capture
)
2133 static constexpr int MaxCaptureAttributes
{9};
2136 case ALC_ATTRIBUTES_SIZE
:
2137 values
[0] = MaxCaptureAttributes
;
2139 case ALC_ALL_ATTRIBUTES
:
2140 if(values
.size() >= MaxCaptureAttributes
)
2143 values
[i
++] = ALC_MAJOR_VERSION
;
2144 values
[i
++] = alcMajorVersion
;
2145 values
[i
++] = ALC_MINOR_VERSION
;
2146 values
[i
++] = alcMinorVersion
;
2147 values
[i
++] = ALC_CAPTURE_SAMPLES
;
2148 values
[i
++] = static_cast<int>(device
->Backend
->availableSamples());
2149 values
[i
++] = ALC_CONNECTED
;
2150 values
[i
++] = device
->Connected
.load(std::memory_order_relaxed
);
2152 assert(i
== MaxCaptureAttributes
);
2155 alcSetError(device
, ALC_INVALID_VALUE
);
2158 case ALC_MAJOR_VERSION
:
2159 values
[0] = alcMajorVersion
;
2161 case ALC_MINOR_VERSION
:
2162 values
[0] = alcMinorVersion
;
2165 case ALC_CAPTURE_SAMPLES
:
2166 values
[0] = static_cast<int>(device
->Backend
->availableSamples());
2170 values
[0] = device
->Connected
.load(std::memory_order_acquire
);
2174 alcSetError(device
, ALC_INVALID_ENUM
);
2180 auto NumAttrsForDevice
= [](const ALCdevice
*aldev
) noexcept
-> uint8_t
2182 if(aldev
->Type
== DeviceType::Loopback
&& aldev
->FmtChans
== DevFmtAmbi3D
)
2188 case ALC_ATTRIBUTES_SIZE
:
2189 values
[0] = NumAttrsForDevice(device
);
2192 case ALC_ALL_ATTRIBUTES
:
2193 if(values
.size() >= NumAttrsForDevice(device
))
2196 values
[i
++] = ALC_MAJOR_VERSION
;
2197 values
[i
++] = alcMajorVersion
;
2198 values
[i
++] = ALC_MINOR_VERSION
;
2199 values
[i
++] = alcMinorVersion
;
2200 values
[i
++] = ALC_EFX_MAJOR_VERSION
;
2201 values
[i
++] = alcEFXMajorVersion
;
2202 values
[i
++] = ALC_EFX_MINOR_VERSION
;
2203 values
[i
++] = alcEFXMinorVersion
;
2205 values
[i
++] = ALC_FREQUENCY
;
2206 values
[i
++] = static_cast<int>(device
->Frequency
);
2207 if(device
->Type
!= DeviceType::Loopback
)
2209 values
[i
++] = ALC_REFRESH
;
2210 values
[i
++] = static_cast<int>(device
->Frequency
/ device
->UpdateSize
);
2212 values
[i
++] = ALC_SYNC
;
2213 values
[i
++] = ALC_FALSE
;
2217 if(device
->FmtChans
== DevFmtAmbi3D
)
2219 values
[i
++] = ALC_AMBISONIC_LAYOUT_SOFT
;
2220 values
[i
++] = EnumFromDevAmbi(device
->mAmbiLayout
);
2222 values
[i
++] = ALC_AMBISONIC_SCALING_SOFT
;
2223 values
[i
++] = EnumFromDevAmbi(device
->mAmbiScale
);
2225 values
[i
++] = ALC_AMBISONIC_ORDER_SOFT
;
2226 values
[i
++] = static_cast<int>(device
->mAmbiOrder
);
2229 values
[i
++] = ALC_FORMAT_CHANNELS_SOFT
;
2230 values
[i
++] = EnumFromDevFmt(device
->FmtChans
);
2232 values
[i
++] = ALC_FORMAT_TYPE_SOFT
;
2233 values
[i
++] = EnumFromDevFmt(device
->FmtType
);
2236 values
[i
++] = ALC_MONO_SOURCES
;
2237 values
[i
++] = static_cast<int>(device
->NumMonoSources
);
2239 values
[i
++] = ALC_STEREO_SOURCES
;
2240 values
[i
++] = static_cast<int>(device
->NumStereoSources
);
2242 values
[i
++] = ALC_MAX_AUXILIARY_SENDS
;
2243 values
[i
++] = static_cast<int>(device
->NumAuxSends
);
2245 values
[i
++] = ALC_HRTF_SOFT
;
2246 values
[i
++] = (device
->mHrtf
? ALC_TRUE
: ALC_FALSE
);
2248 values
[i
++] = ALC_HRTF_STATUS_SOFT
;
2249 values
[i
++] = device
->mHrtfStatus
;
2251 values
[i
++] = ALC_OUTPUT_LIMITER_SOFT
;
2252 values
[i
++] = device
->Limiter
? ALC_TRUE
: ALC_FALSE
;
2254 values
[i
++] = ALC_MAX_AMBISONIC_ORDER_SOFT
;
2255 values
[i
++] = MaxAmbiOrder
;
2257 values
[i
++] = ALC_OUTPUT_MODE_SOFT
;
2258 values
[i
++] = static_cast<ALCenum
>(device
->getOutputMode1());
2261 assert(i
== NumAttrsForDevice(device
));
2264 alcSetError(device
, ALC_INVALID_VALUE
);
2267 case ALC_MAJOR_VERSION
:
2268 values
[0] = alcMajorVersion
;
2271 case ALC_MINOR_VERSION
:
2272 values
[0] = alcMinorVersion
;
2275 case ALC_EFX_MAJOR_VERSION
:
2276 values
[0] = alcEFXMajorVersion
;
2279 case ALC_EFX_MINOR_VERSION
:
2280 values
[0] = alcEFXMinorVersion
;
2284 values
[0] = static_cast<int>(device
->Frequency
);
2288 if(device
->Type
== DeviceType::Loopback
)
2290 alcSetError(device
, ALC_INVALID_DEVICE
);
2293 values
[0] = static_cast<int>(device
->Frequency
/ device
->UpdateSize
);
2297 if(device
->Type
== DeviceType::Loopback
)
2299 alcSetError(device
, ALC_INVALID_DEVICE
);
2302 values
[0] = ALC_FALSE
;
2305 case ALC_FORMAT_CHANNELS_SOFT
:
2306 if(device
->Type
!= DeviceType::Loopback
)
2308 alcSetError(device
, ALC_INVALID_DEVICE
);
2311 values
[0] = EnumFromDevFmt(device
->FmtChans
);
2314 case ALC_FORMAT_TYPE_SOFT
:
2315 if(device
->Type
!= DeviceType::Loopback
)
2317 alcSetError(device
, ALC_INVALID_DEVICE
);
2320 values
[0] = EnumFromDevFmt(device
->FmtType
);
2323 case ALC_AMBISONIC_LAYOUT_SOFT
:
2324 if(device
->Type
!= DeviceType::Loopback
|| device
->FmtChans
!= DevFmtAmbi3D
)
2326 alcSetError(device
, ALC_INVALID_DEVICE
);
2329 values
[0] = EnumFromDevAmbi(device
->mAmbiLayout
);
2332 case ALC_AMBISONIC_SCALING_SOFT
:
2333 if(device
->Type
!= DeviceType::Loopback
|| device
->FmtChans
!= DevFmtAmbi3D
)
2335 alcSetError(device
, ALC_INVALID_DEVICE
);
2338 values
[0] = EnumFromDevAmbi(device
->mAmbiScale
);
2341 case ALC_AMBISONIC_ORDER_SOFT
:
2342 if(device
->Type
!= DeviceType::Loopback
|| device
->FmtChans
!= DevFmtAmbi3D
)
2344 alcSetError(device
, ALC_INVALID_DEVICE
);
2347 values
[0] = static_cast<int>(device
->mAmbiOrder
);
2350 case ALC_MONO_SOURCES
:
2351 values
[0] = static_cast<int>(device
->NumMonoSources
);
2354 case ALC_STEREO_SOURCES
:
2355 values
[0] = static_cast<int>(device
->NumStereoSources
);
2358 case ALC_MAX_AUXILIARY_SENDS
:
2359 values
[0] = static_cast<int>(device
->NumAuxSends
);
2363 values
[0] = device
->Connected
.load(std::memory_order_acquire
);
2367 values
[0] = (device
->mHrtf
? ALC_TRUE
: ALC_FALSE
);
2370 case ALC_HRTF_STATUS_SOFT
:
2371 values
[0] = device
->mHrtfStatus
;
2374 case ALC_NUM_HRTF_SPECIFIERS_SOFT
:
2375 device
->enumerateHrtfs();
2376 values
[0] = static_cast<int>(minz(device
->mHrtfList
.size(),
2377 std::numeric_limits
<int>::max()));
2380 case ALC_OUTPUT_LIMITER_SOFT
:
2381 values
[0] = device
->Limiter
? ALC_TRUE
: ALC_FALSE
;
2384 case ALC_MAX_AMBISONIC_ORDER_SOFT
:
2385 values
[0] = MaxAmbiOrder
;
2388 case ALC_OUTPUT_MODE_SOFT
:
2389 values
[0] = static_cast<ALCenum
>(device
->getOutputMode1());
2393 alcSetError(device
, ALC_INVALID_ENUM
);
2398 ALC_API
void ALC_APIENTRY
alcGetIntegerv(ALCdevice
*device
, ALCenum param
, ALCsizei size
, ALCint
*values
) noexcept
2400 DeviceRef dev
{VerifyDevice(device
)};
2401 if(size
<= 0 || values
== nullptr)
2402 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2404 GetIntegerv(dev
.get(), param
, {values
, static_cast<uint
>(size
)});
2407 ALC_API
void ALC_APIENTRY
alcGetInteger64vSOFT(ALCdevice
*device
, ALCenum pname
, ALCsizei size
, ALCint64SOFT
*values
) noexcept
2409 DeviceRef dev
{VerifyDevice(device
)};
2410 if(size
<= 0 || values
== nullptr)
2412 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2415 if(!dev
|| dev
->Type
== DeviceType::Capture
)
2417 auto ivals
= std::vector
<int>(static_cast<uint
>(size
));
2418 if(size_t got
{GetIntegerv(dev
.get(), pname
, ivals
)})
2419 std::copy_n(ivals
.begin(), got
, values
);
2423 auto NumAttrsForDevice
= [](ALCdevice
*aldev
) noexcept
2425 if(aldev
->Type
== DeviceType::Loopback
&& aldev
->FmtChans
== DevFmtAmbi3D
)
2429 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
2432 case ALC_ATTRIBUTES_SIZE
:
2433 *values
= NumAttrsForDevice(dev
.get());
2436 case ALC_ALL_ATTRIBUTES
:
2437 if(size
< NumAttrsForDevice(dev
.get()))
2438 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2442 values
[i
++] = ALC_FREQUENCY
;
2443 values
[i
++] = dev
->Frequency
;
2445 if(dev
->Type
!= DeviceType::Loopback
)
2447 values
[i
++] = ALC_REFRESH
;
2448 values
[i
++] = dev
->Frequency
/ dev
->UpdateSize
;
2450 values
[i
++] = ALC_SYNC
;
2451 values
[i
++] = ALC_FALSE
;
2455 values
[i
++] = ALC_FORMAT_CHANNELS_SOFT
;
2456 values
[i
++] = EnumFromDevFmt(dev
->FmtChans
);
2458 values
[i
++] = ALC_FORMAT_TYPE_SOFT
;
2459 values
[i
++] = EnumFromDevFmt(dev
->FmtType
);
2461 if(dev
->FmtChans
== DevFmtAmbi3D
)
2463 values
[i
++] = ALC_AMBISONIC_LAYOUT_SOFT
;
2464 values
[i
++] = EnumFromDevAmbi(dev
->mAmbiLayout
);
2466 values
[i
++] = ALC_AMBISONIC_SCALING_SOFT
;
2467 values
[i
++] = EnumFromDevAmbi(dev
->mAmbiScale
);
2469 values
[i
++] = ALC_AMBISONIC_ORDER_SOFT
;
2470 values
[i
++] = dev
->mAmbiOrder
;
2474 values
[i
++] = ALC_MONO_SOURCES
;
2475 values
[i
++] = dev
->NumMonoSources
;
2477 values
[i
++] = ALC_STEREO_SOURCES
;
2478 values
[i
++] = dev
->NumStereoSources
;
2480 values
[i
++] = ALC_MAX_AUXILIARY_SENDS
;
2481 values
[i
++] = dev
->NumAuxSends
;
2483 values
[i
++] = ALC_HRTF_SOFT
;
2484 values
[i
++] = (dev
->mHrtf
? ALC_TRUE
: ALC_FALSE
);
2486 values
[i
++] = ALC_HRTF_STATUS_SOFT
;
2487 values
[i
++] = dev
->mHrtfStatus
;
2489 values
[i
++] = ALC_OUTPUT_LIMITER_SOFT
;
2490 values
[i
++] = dev
->Limiter
? ALC_TRUE
: ALC_FALSE
;
2492 ClockLatency clock
{GetClockLatency(dev
.get(), dev
->Backend
.get())};
2493 values
[i
++] = ALC_DEVICE_CLOCK_SOFT
;
2494 values
[i
++] = clock
.ClockTime
.count();
2496 values
[i
++] = ALC_DEVICE_LATENCY_SOFT
;
2497 values
[i
++] = clock
.Latency
.count();
2499 values
[i
++] = ALC_OUTPUT_MODE_SOFT
;
2500 values
[i
++] = al::to_underlying(device
->getOutputMode1());
2506 case ALC_DEVICE_CLOCK_SOFT
:
2508 uint samplecount
, refcount
;
2509 nanoseconds basecount
;
2511 refcount
= dev
->waitForMix();
2512 basecount
= dev
->mClockBase
.load(std::memory_order_relaxed
);
2513 samplecount
= dev
->mSamplesDone
.load(std::memory_order_relaxed
);
2514 std::atomic_thread_fence(std::memory_order_acquire
);
2515 } while(refcount
!= dev
->mMixCount
.load(std::memory_order_relaxed
));
2516 basecount
+= nanoseconds
{seconds
{samplecount
}} / dev
->Frequency
;
2517 *values
= basecount
.count();
2521 case ALC_DEVICE_LATENCY_SOFT
:
2522 *values
= GetClockLatency(dev
.get(), dev
->Backend
.get()).Latency
.count();
2525 case ALC_DEVICE_CLOCK_LATENCY_SOFT
:
2527 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2530 ClockLatency clock
{GetClockLatency(dev
.get(), dev
->Backend
.get())};
2531 values
[0] = clock
.ClockTime
.count();
2532 values
[1] = clock
.Latency
.count();
2537 auto ivals
= std::vector
<int>(static_cast<uint
>(size
));
2538 if(size_t got
{GetIntegerv(dev
.get(), pname
, ivals
)})
2539 std::copy_n(ivals
.begin(), got
, values
);
2545 ALC_API ALCboolean ALC_APIENTRY
alcIsExtensionPresent(ALCdevice
*device
, const ALCchar
*extName
) noexcept
2547 DeviceRef dev
{VerifyDevice(device
)};
2550 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2554 const std::string_view tofind
{extName
};
2555 const auto extlist
= dev
? GetExtensionList() : GetNoDeviceExtList();
2556 auto matchpos
= extlist
.find(tofind
);
2557 while(matchpos
!= std::string_view::npos
)
2559 const auto endpos
= matchpos
+ tofind
.size();
2560 if((matchpos
== 0 || std::isspace(extlist
[matchpos
-1]))
2561 && (endpos
== extlist
.size() || std::isspace(extlist
[endpos
])))
2563 matchpos
= extlist
.find(tofind
, matchpos
+1);
2569 ALCvoid
* ALC_APIENTRY
alcGetProcAddress2(ALCdevice
*device
, const ALCchar
*funcName
) noexcept
2570 { return alcGetProcAddress(device
, funcName
); }
2572 ALC_API ALCvoid
* ALC_APIENTRY
alcGetProcAddress(ALCdevice
*device
, const ALCchar
*funcName
) noexcept
2576 DeviceRef dev
{VerifyDevice(device
)};
2577 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2582 if(eax_g_is_enabled
)
2584 for(const auto &func
: eaxFunctions
)
2586 if(strcmp(func
.funcName
, funcName
) == 0)
2587 return func
.address
;
2591 for(const auto &func
: alcFunctions
)
2593 if(strcmp(func
.funcName
, funcName
) == 0)
2594 return func
.address
;
2600 ALC_API ALCenum ALC_APIENTRY
alcGetEnumValue(ALCdevice
*device
, const ALCchar
*enumName
) noexcept
2604 DeviceRef dev
{VerifyDevice(device
)};
2605 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
2610 if(eax_g_is_enabled
)
2612 for(const auto &enm
: eaxEnumerations
)
2614 if(strcmp(enm
.enumName
, enumName
) == 0)
2619 for(const auto &enm
: alcEnumerations
)
2621 if(strcmp(enm
.enumName
, enumName
) == 0)
2629 ALC_API ALCcontext
* ALC_APIENTRY
alcCreateContext(ALCdevice
*device
, const ALCint
*attrList
) noexcept
2631 /* Explicitly hold the list lock while taking the StateLock in case the
2632 * device is asynchronously destroyed, to ensure this new context is
2633 * properly cleaned up after being made.
2635 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
2636 DeviceRef dev
{VerifyDevice(device
)};
2637 if(!dev
|| dev
->Type
== DeviceType::Capture
|| !dev
->Connected
.load(std::memory_order_relaxed
))
2640 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
2643 std::unique_lock
<std::mutex
> statelock
{dev
->StateLock
};
2646 dev
->LastError
.store(ALC_NO_ERROR
);
2648 ALCenum err
{UpdateDeviceParams(dev
.get(), attrList
)};
2649 if(err
!= ALC_NO_ERROR
)
2651 alcSetError(dev
.get(), err
);
2655 ContextFlagBitset ctxflags
{0};
2658 for(size_t i
{0};attrList
[i
];i
+=2)
2660 if(attrList
[i
] == ALC_CONTEXT_FLAGS_EXT
)
2662 ctxflags
= static_cast<ALuint
>(attrList
[i
+1]);
2668 ContextRef context
{[](auto&& ...args
) -> ContextRef
2671 return ContextRef
{new ALCcontext
{std::forward
<decltype(args
)>(args
)...}};
2673 catch(std::exception
& e
) {
2674 ERR("Failed to create ALCcontext: %s\n", e
.what());
2675 return ContextRef
{};
2680 alcSetError(dev
.get(), ALC_OUT_OF_MEMORY
);
2685 if(auto volopt
= dev
->configValue
<float>({}, "volume-adjust"))
2687 const float valf
{*volopt
};
2688 if(!std::isfinite(valf
))
2689 ERR("volume-adjust must be finite: %f\n", valf
);
2692 const float db
{clampf(valf
, -24.0f
, 24.0f
)};
2694 WARN("volume-adjust clamped: %f, range: +/-%f\n", valf
, 24.0f
);
2695 context
->mGainBoost
= std::pow(10.0f
, db
/20.0f
);
2696 TRACE("volume-adjust gain: %f\n", context
->mGainBoost
);
2701 using ContextArray
= al::FlexArray
<ContextBase
*>;
2703 /* Allocate a new context array, which holds 1 more than the current/
2706 auto *oldarray
= device
->mContexts
.load();
2707 auto newarray
= ContextArray::Create(oldarray
->size() + 1);
2709 /* Copy the current/old context handles to the new array, appending the
2712 auto iter
= std::copy(oldarray
->begin(), oldarray
->end(), newarray
->begin());
2713 *iter
= context
.get();
2715 /* Store the new context array in the device. Wait for any current mix
2716 * to finish before deleting the old array.
2718 auto prevarray
= dev
->mContexts
.exchange(std::move(newarray
));
2719 std::ignore
= dev
->waitForMix();
2725 auto iter
= std::lower_bound(ContextList
.cbegin(), ContextList
.cend(), context
.get());
2726 ContextList
.emplace(iter
, context
.get());
2730 if(ALeffectslot
*slot
{context
->mDefaultSlot
.get()})
2732 ALenum sloterr
{slot
->initEffect(0, ALCcontext::sDefaultEffect
.type
,
2733 ALCcontext::sDefaultEffect
.Props
, context
.get())};
2734 if(sloterr
== AL_NO_ERROR
)
2735 slot
->updateProps(context
.get());
2737 ERR("Failed to initialize the default effect\n");
2740 TRACE("Created context %p\n", voidp
{context
.get()});
2741 return context
.release();
2744 ALC_API
void ALC_APIENTRY
alcDestroyContext(ALCcontext
*context
) noexcept
2746 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
2747 auto iter
= std::lower_bound(ContextList
.begin(), ContextList
.end(), context
);
2748 if(iter
== ContextList
.end() || *iter
!= context
)
2751 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2755 /* Hold a reference to this context so it remains valid until the ListLock
2758 ContextRef ctx
{*iter
};
2759 ContextList
.erase(iter
);
2761 ALCdevice
*Device
{ctx
->mALDevice
.get()};
2763 std::lock_guard
<std::mutex
> statelock
{Device
->StateLock
};
2768 ALC_API
auto ALC_APIENTRY
alcGetCurrentContext() noexcept
-> ALCcontext
*
2770 ALCcontext
*Context
{ALCcontext::getThreadContext()};
2771 if(!Context
) Context
= ALCcontext::sGlobalContext
.load();
2775 /** Returns the currently active thread-local context. */
2776 ALC_API
auto ALC_APIENTRY
alcGetThreadContext() noexcept
-> ALCcontext
*
2777 { return ALCcontext::getThreadContext(); }
2779 ALC_API ALCboolean ALC_APIENTRY
alcMakeContextCurrent(ALCcontext
*context
) noexcept
2781 /* context must be valid or nullptr */
2785 ctx
= VerifyContext(context
);
2788 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2792 /* Release this reference (if any) to store it in the GlobalContext
2793 * pointer. Take ownership of the reference (if any) that was previously
2794 * stored there, and let the reference go.
2796 while(ALCcontext::sGlobalContextLock
.exchange(true, std::memory_order_acquire
)) {
2797 /* Wait to make sure another thread isn't getting or trying to change
2798 * the current context as its refcount is decremented.
2801 ctx
= ContextRef
{ALCcontext::sGlobalContext
.exchange(ctx
.release())};
2802 ALCcontext::sGlobalContextLock
.store(false, std::memory_order_release
);
2804 /* Take ownership of the thread-local context reference (if any), clearing
2805 * the storage to null.
2807 ctx
= ContextRef
{ALCcontext::getThreadContext()};
2808 if(ctx
) ALCcontext::setThreadContext(nullptr);
2809 /* Reset (decrement) the previous thread-local reference. */
2814 /** Makes the given context the active context for the current thread. */
2815 ALC_API ALCboolean ALC_APIENTRY
alcSetThreadContext(ALCcontext
*context
) noexcept
2817 /* context must be valid or nullptr */
2821 ctx
= VerifyContext(context
);
2824 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2828 /* context's reference count is already incremented */
2829 ContextRef old
{ALCcontext::getThreadContext()};
2830 ALCcontext::setThreadContext(ctx
.release());
2836 ALC_API ALCdevice
* ALC_APIENTRY
alcGetContextsDevice(ALCcontext
*Context
) noexcept
2838 ContextRef ctx
{VerifyContext(Context
)};
2841 alcSetError(nullptr, ALC_INVALID_CONTEXT
);
2844 return ctx
->mALDevice
.get();
2848 ALC_API ALCdevice
* ALC_APIENTRY
alcOpenDevice(const ALCchar
*deviceName
) noexcept
2852 if(!PlaybackFactory
)
2854 alcSetError(nullptr, ALC_INVALID_VALUE
);
2858 /* We need to ensure the device name isn't too long. The string_view is
2859 * printed using the "%.*s" formatter, which uses an int for the precision/
2860 * length. It wouldn't be a significant problem if larger values simply
2861 * printed fewer characters due to truncation, but negative values are
2862 * ignored, treating it like a normal null-terminated string, and
2863 * string_views don't need to be null-terminated.
2865 * Other than the annoyance of checking, this shouldn't be a problem. Two
2866 * billion bytes is enough for a device name.
2868 std::string_view devname
{deviceName
? deviceName
: ""};
2869 if(!devname
.empty())
2871 if(devname
.length() >= std::numeric_limits
<int>::max())
2873 ERR("Device name too long (%zu >= %d)\n", devname
.length(),
2874 std::numeric_limits
<int>::max());
2875 alcSetError(nullptr, ALC_INVALID_VALUE
);
2879 TRACE("Opening playback device \"%.*s\"\n", static_cast<int>(devname
.size()),
2881 if(al::case_compare(devname
, GetDefaultName()) == 0
2883 /* Some old Windows apps hardcode these expecting OpenAL to use a
2884 * specific audio API, even when they're not enumerated. Creative's
2885 * router effectively ignores them too.
2887 || al::case_compare(devname
, "DirectSound3D"sv
) == 0
2888 || al::case_compare(devname
, "DirectSound"sv
) == 0
2889 || al::case_compare(devname
, "MMSYSTEM"sv
) == 0
2891 /* Some old Linux apps hardcode configuration strings that were
2892 * supported by the OpenAL SI. We can't really do anything useful
2893 * with them, so just ignore.
2895 || al::starts_with(devname
, "'("sv
)
2896 || al::case_compare(devname
, "openal-soft"sv
) == 0)
2900 TRACE("Opening default playback device\n");
2902 const uint DefaultSends
{
2904 eax_g_is_enabled
? uint
{EAX_MAX_FXSLOTS
} :
2905 #endif // ALSOFT_EAX
2906 uint
{DefaultSendCount
}
2909 DeviceRef device
{new(std::nothrow
) ALCdevice
{DeviceType::Playback
}};
2912 WARN("Failed to create playback device handle\n");
2913 alcSetError(nullptr, ALC_OUT_OF_MEMORY
);
2917 /* Set output format */
2918 device
->FmtChans
= DevFmtChannelsDefault
;
2919 device
->FmtType
= DevFmtTypeDefault
;
2920 device
->Frequency
= DefaultOutputRate
;
2921 device
->UpdateSize
= DefaultUpdateSize
;
2922 device
->BufferSize
= DefaultUpdateSize
* DefaultNumUpdates
;
2924 device
->SourcesMax
= 256;
2925 device
->NumStereoSources
= 1;
2926 device
->NumMonoSources
= device
->SourcesMax
- device
->NumStereoSources
;
2927 device
->AuxiliaryEffectSlotMax
= 64;
2928 device
->NumAuxSends
= DefaultSends
;
2931 auto backend
= PlaybackFactory
->createBackend(device
.get(), BackendType::Playback
);
2932 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
2933 backend
->open(devname
);
2934 device
->Backend
= std::move(backend
);
2936 catch(al::backend_exception
&e
) {
2937 WARN("Failed to open playback device: %s\n", e
.what());
2938 alcSetError(nullptr, (e
.errorCode() == al::backend_error::OutOfMemory
)
2939 ? ALC_OUT_OF_MEMORY
: ALC_INVALID_VALUE
);
2944 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
2945 auto iter
= std::lower_bound(DeviceList
.cbegin(), DeviceList
.cend(), device
.get());
2946 DeviceList
.emplace(iter
, device
.get());
2949 TRACE("Created device %p, \"%s\"\n", voidp
{device
.get()}, device
->DeviceName
.c_str());
2950 return device
.release();
2953 ALC_API ALCboolean ALC_APIENTRY
alcCloseDevice(ALCdevice
*device
) noexcept
2955 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
2956 auto iter
= std::lower_bound(DeviceList
.begin(), DeviceList
.end(), device
);
2957 if(iter
== DeviceList
.end() || *iter
!= device
)
2959 alcSetError(nullptr, ALC_INVALID_DEVICE
);
2962 if((*iter
)->Type
== DeviceType::Capture
)
2964 alcSetError(*iter
, ALC_INVALID_DEVICE
);
2968 /* Erase the device, and any remaining contexts left on it, from their
2971 DeviceRef dev
{*iter
};
2972 DeviceList
.erase(iter
);
2974 std::unique_lock
<std::mutex
> statelock
{dev
->StateLock
};
2975 std::vector
<ContextRef
> orphanctxs
;
2976 for(ContextBase
*ctx
: *dev
->mContexts
.load())
2978 auto ctxiter
= std::lower_bound(ContextList
.begin(), ContextList
.end(), ctx
);
2979 if(ctxiter
!= ContextList
.end() && *ctxiter
== ctx
)
2981 orphanctxs
.emplace_back(*ctxiter
);
2982 ContextList
.erase(ctxiter
);
2987 for(ContextRef
&context
: orphanctxs
)
2989 WARN("Releasing orphaned context %p\n", voidp
{context
.get()});
2994 if(dev
->mDeviceState
== DeviceState::Playing
)
2996 dev
->Backend
->stop();
2997 dev
->mDeviceState
= DeviceState::Configured
;
3004 /************************************************
3005 * ALC capture functions
3006 ************************************************/
3007 ALC_API ALCdevice
* ALC_APIENTRY
alcCaptureOpenDevice(const ALCchar
*deviceName
, ALCuint frequency
, ALCenum format
, ALCsizei samples
) noexcept
3013 alcSetError(nullptr, ALC_INVALID_VALUE
);
3019 alcSetError(nullptr, ALC_INVALID_VALUE
);
3023 std::string_view devname
{deviceName
? deviceName
: ""};
3024 if(!devname
.empty())
3026 if(devname
.length() >= std::numeric_limits
<int>::max())
3028 ERR("Device name too long (%zu >= %d)\n", devname
.length(),
3029 std::numeric_limits
<int>::max());
3030 alcSetError(nullptr, ALC_INVALID_VALUE
);
3034 TRACE("Opening capture device \"%.*s\"\n", static_cast<int>(devname
.size()),
3036 if(al::case_compare(devname
, GetDefaultName()) == 0
3037 || al::case_compare(devname
, "openal-soft"sv
) == 0)
3041 TRACE("Opening default capture device\n");
3043 DeviceRef device
{new(std::nothrow
) ALCdevice
{DeviceType::Capture
}};
3046 WARN("Failed to create capture device handle\n");
3047 alcSetError(nullptr, ALC_OUT_OF_MEMORY
);
3051 auto decompfmt
= DecomposeDevFormat(format
);
3054 alcSetError(nullptr, ALC_INVALID_ENUM
);
3058 device
->Frequency
= frequency
;
3059 device
->FmtChans
= decompfmt
->chans
;
3060 device
->FmtType
= decompfmt
->type
;
3061 device
->Flags
.set(FrequencyRequest
);
3062 device
->Flags
.set(ChannelsRequest
);
3063 device
->Flags
.set(SampleTypeRequest
);
3065 device
->UpdateSize
= static_cast<uint
>(samples
);
3066 device
->BufferSize
= static_cast<uint
>(samples
);
3068 TRACE("Capture format: %s, %s, %uhz, %u / %u buffer\n", DevFmtChannelsString(device
->FmtChans
),
3069 DevFmtTypeString(device
->FmtType
), device
->Frequency
, device
->UpdateSize
,
3070 device
->BufferSize
);
3073 auto backend
= CaptureFactory
->createBackend(device
.get(), BackendType::Capture
);
3074 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
3075 backend
->open(devname
);
3076 device
->Backend
= std::move(backend
);
3078 catch(al::backend_exception
&e
) {
3079 WARN("Failed to open capture device: %s\n", e
.what());
3080 alcSetError(nullptr, (e
.errorCode() == al::backend_error::OutOfMemory
)
3081 ? ALC_OUT_OF_MEMORY
: ALC_INVALID_VALUE
);
3086 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
3087 auto iter
= std::lower_bound(DeviceList
.cbegin(), DeviceList
.cend(), device
.get());
3088 DeviceList
.emplace(iter
, device
.get());
3090 device
->mDeviceState
= DeviceState::Configured
;
3092 TRACE("Created capture device %p, \"%s\"\n", voidp
{device
.get()}, device
->DeviceName
.c_str());
3093 return device
.release();
3096 ALC_API ALCboolean ALC_APIENTRY
alcCaptureCloseDevice(ALCdevice
*device
) noexcept
3098 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
3099 auto iter
= std::lower_bound(DeviceList
.begin(), DeviceList
.end(), device
);
3100 if(iter
== DeviceList
.end() || *iter
!= device
)
3102 alcSetError(nullptr, ALC_INVALID_DEVICE
);
3105 if((*iter
)->Type
!= DeviceType::Capture
)
3107 alcSetError(*iter
, ALC_INVALID_DEVICE
);
3111 DeviceRef dev
{*iter
};
3112 DeviceList
.erase(iter
);
3115 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3116 if(dev
->mDeviceState
== DeviceState::Playing
)
3118 dev
->Backend
->stop();
3119 dev
->mDeviceState
= DeviceState::Configured
;
3125 ALC_API
void ALC_APIENTRY
alcCaptureStart(ALCdevice
*device
) noexcept
3127 DeviceRef dev
{VerifyDevice(device
)};
3128 if(!dev
|| dev
->Type
!= DeviceType::Capture
)
3130 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3134 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3135 if(!dev
->Connected
.load(std::memory_order_acquire
)
3136 || dev
->mDeviceState
< DeviceState::Configured
)
3137 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3138 else if(dev
->mDeviceState
!= DeviceState::Playing
)
3141 auto backend
= dev
->Backend
.get();
3143 dev
->mDeviceState
= DeviceState::Playing
;
3145 catch(al::backend_exception
& e
) {
3146 ERR("%s\n", e
.what());
3147 dev
->handleDisconnect("%s", e
.what());
3148 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3153 ALC_API
void ALC_APIENTRY
alcCaptureStop(ALCdevice
*device
) noexcept
3155 DeviceRef dev
{VerifyDevice(device
)};
3156 if(!dev
|| dev
->Type
!= DeviceType::Capture
)
3157 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3160 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3161 if(dev
->mDeviceState
== DeviceState::Playing
)
3163 dev
->Backend
->stop();
3164 dev
->mDeviceState
= DeviceState::Configured
;
3169 ALC_API
void ALC_APIENTRY
alcCaptureSamples(ALCdevice
*device
, ALCvoid
*buffer
, ALCsizei samples
) noexcept
3171 DeviceRef dev
{VerifyDevice(device
)};
3172 if(!dev
|| dev
->Type
!= DeviceType::Capture
)
3174 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3178 if(samples
< 0 || (samples
> 0 && buffer
== nullptr))
3180 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3186 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3187 BackendBase
*backend
{dev
->Backend
.get()};
3189 const auto usamples
= static_cast<uint
>(samples
);
3190 if(usamples
> backend
->availableSamples())
3192 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3196 backend
->captureSamples(static_cast<std::byte
*>(buffer
), usamples
);
3200 /************************************************
3201 * ALC loopback functions
3202 ************************************************/
3204 /** Open a loopback device, for manual rendering. */
3205 ALC_API ALCdevice
* ALC_APIENTRY
alcLoopbackOpenDeviceSOFT(const ALCchar
*deviceName
) noexcept
3209 /* Make sure the device name, if specified, is us. */
3210 if(deviceName
&& strcmp(deviceName
, GetDefaultName()) != 0)
3212 alcSetError(nullptr, ALC_INVALID_VALUE
);
3216 const uint DefaultSends
{
3218 eax_g_is_enabled
? uint
{EAX_MAX_FXSLOTS
} :
3219 #endif // ALSOFT_EAX
3220 uint
{DefaultSendCount
}
3223 DeviceRef device
{new(std::nothrow
) ALCdevice
{DeviceType::Loopback
}};
3226 WARN("Failed to create loopback device handle\n");
3227 alcSetError(nullptr, ALC_OUT_OF_MEMORY
);
3231 device
->SourcesMax
= 256;
3232 device
->AuxiliaryEffectSlotMax
= 64;
3233 device
->NumAuxSends
= DefaultSends
;
3236 device
->BufferSize
= 0;
3237 device
->UpdateSize
= 0;
3239 device
->Frequency
= DefaultOutputRate
;
3240 device
->FmtChans
= DevFmtChannelsDefault
;
3241 device
->FmtType
= DevFmtTypeDefault
;
3243 device
->NumStereoSources
= 1;
3244 device
->NumMonoSources
= device
->SourcesMax
- device
->NumStereoSources
;
3247 auto backend
= LoopbackBackendFactory::getFactory().createBackend(device
.get(),
3248 BackendType::Playback
);
3249 backend
->open("Loopback");
3250 device
->Backend
= std::move(backend
);
3252 catch(al::backend_exception
&e
) {
3253 WARN("Failed to open loopback device: %s\n", e
.what());
3254 alcSetError(nullptr, (e
.errorCode() == al::backend_error::OutOfMemory
)
3255 ? ALC_OUT_OF_MEMORY
: ALC_INVALID_VALUE
);
3260 std::lock_guard
<std::recursive_mutex
> listlock
{ListLock
};
3261 auto iter
= std::lower_bound(DeviceList
.cbegin(), DeviceList
.cend(), device
.get());
3262 DeviceList
.emplace(iter
, device
.get());
3265 TRACE("Created loopback device %p\n", voidp
{device
.get()});
3266 return device
.release();
3270 * Determines if the loopback device supports the given format for rendering.
3272 ALC_API ALCboolean ALC_APIENTRY
alcIsRenderFormatSupportedSOFT(ALCdevice
*device
, ALCsizei freq
, ALCenum channels
, ALCenum type
) noexcept
3274 DeviceRef dev
{VerifyDevice(device
)};
3275 if(!dev
|| dev
->Type
!= DeviceType::Loopback
)
3276 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3278 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3281 if(DevFmtTypeFromEnum(type
).has_value() && DevFmtChannelsFromEnum(channels
).has_value()
3282 && freq
>= int{MinOutputRate
} && freq
<= int{MaxOutputRate
})
3290 * Renders some samples into a buffer, using the format last set by the
3291 * attributes given to alcCreateContext.
3293 #if defined(__GNUC__) && defined(__i386__)
3294 /* Needed on x86-32 even without SSE codegen, since the mixer may still use SSE
3295 * and GCC assumes the stack is aligned (x86-64 ABI guarantees alignment).
3297 [[gnu::force_align_arg_pointer
]]
3299 ALC_API
void ALC_APIENTRY
alcRenderSamplesSOFT(ALCdevice
*device
, ALCvoid
*buffer
, ALCsizei samples
) noexcept
3301 if(!device
|| device
->Type
!= DeviceType::Loopback
) UNLIKELY
3302 alcSetError(device
, ALC_INVALID_DEVICE
);
3303 else if(samples
< 0 || (samples
> 0 && buffer
== nullptr)) UNLIKELY
3304 alcSetError(device
, ALC_INVALID_VALUE
);
3306 device
->renderSamples(buffer
, static_cast<uint
>(samples
), device
->channelsFromFmt());
3310 /************************************************
3311 * ALC DSP pause/resume functions
3312 ************************************************/
3314 /** Pause the DSP to stop audio processing. */
3315 ALC_API
void ALC_APIENTRY
alcDevicePauseSOFT(ALCdevice
*device
) noexcept
3317 DeviceRef dev
{VerifyDevice(device
)};
3318 if(!dev
|| dev
->Type
!= DeviceType::Playback
)
3319 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3322 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3323 if(dev
->mDeviceState
== DeviceState::Playing
)
3325 dev
->Backend
->stop();
3326 dev
->mDeviceState
= DeviceState::Configured
;
3328 dev
->Flags
.set(DevicePaused
);
3332 /** Resume the DSP to restart audio processing. */
3333 ALC_API
void ALC_APIENTRY
alcDeviceResumeSOFT(ALCdevice
*device
) noexcept
3335 DeviceRef dev
{VerifyDevice(device
)};
3336 if(!dev
|| dev
->Type
!= DeviceType::Playback
)
3338 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3342 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3343 if(!dev
->Flags
.test(DevicePaused
))
3345 if(dev
->mDeviceState
< DeviceState::Configured
)
3347 WARN("Cannot resume unconfigured device\n");
3348 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3351 if(!dev
->Connected
.load())
3353 WARN("Cannot resume a disconnected device\n");
3354 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3357 dev
->Flags
.reset(DevicePaused
);
3358 if(dev
->mContexts
.load()->empty())
3362 auto backend
= dev
->Backend
.get();
3364 dev
->mDeviceState
= DeviceState::Playing
;
3366 catch(al::backend_exception
& e
) {
3367 ERR("%s\n", e
.what());
3368 dev
->handleDisconnect("%s", e
.what());
3369 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3372 TRACE("Post-resume: %s, %s, %uhz, %u / %u buffer\n",
3373 DevFmtChannelsString(dev
->FmtChans
), DevFmtTypeString(dev
->FmtType
),
3374 dev
->Frequency
, dev
->UpdateSize
, dev
->BufferSize
);
3378 /************************************************
3379 * ALC HRTF functions
3380 ************************************************/
3382 /** Gets a string parameter at the given index. */
3383 ALC_API
const ALCchar
* ALC_APIENTRY
alcGetStringiSOFT(ALCdevice
*device
, ALCenum paramName
, ALCsizei index
) noexcept
3385 DeviceRef dev
{VerifyDevice(device
)};
3386 if(!dev
|| dev
->Type
== DeviceType::Capture
)
3387 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3388 else switch(paramName
)
3390 case ALC_HRTF_SPECIFIER_SOFT
:
3391 if(index
>= 0 && static_cast<uint
>(index
) < dev
->mHrtfList
.size())
3392 return dev
->mHrtfList
[static_cast<uint
>(index
)].c_str();
3393 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3397 alcSetError(dev
.get(), ALC_INVALID_ENUM
);
3404 /** Resets the given device output, using the specified attribute list. */
3405 ALC_API ALCboolean ALC_APIENTRY
alcResetDeviceSOFT(ALCdevice
*device
, const ALCint
*attribs
) noexcept
3407 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
3408 DeviceRef dev
{VerifyDevice(device
)};
3409 if(!dev
|| dev
->Type
== DeviceType::Capture
)
3412 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3415 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3418 /* Force the backend to stop mixing first since we're resetting. Also reset
3419 * the connected state so lost devices can attempt recover.
3421 if(dev
->mDeviceState
== DeviceState::Playing
)
3423 dev
->Backend
->stop();
3424 dev
->mDeviceState
= DeviceState::Configured
;
3427 return ResetDeviceParams(dev
.get(), attribs
) ? ALC_TRUE
: ALC_FALSE
;
3431 /************************************************
3432 * ALC device reopen functions
3433 ************************************************/
3435 /** Reopens the given device output, using the specified name and attribute list. */
3436 FORCE_ALIGN ALCboolean ALC_APIENTRY
alcReopenDeviceSOFT(ALCdevice
*device
,
3437 const ALCchar
*deviceName
, const ALCint
*attribs
) noexcept
3439 std::unique_lock
<std::recursive_mutex
> listlock
{ListLock
};
3440 DeviceRef dev
{VerifyDevice(device
)};
3441 if(!dev
|| dev
->Type
!= DeviceType::Playback
)
3444 alcSetError(dev
.get(), ALC_INVALID_DEVICE
);
3447 std::lock_guard
<std::mutex
> statelock
{dev
->StateLock
};
3449 std::string_view devname
{deviceName
? deviceName
: ""};
3450 if(!devname
.empty())
3452 if(devname
.length() >= std::numeric_limits
<int>::max())
3454 ERR("Device name too long (%zu >= %d)\n", devname
.length(),
3455 std::numeric_limits
<int>::max());
3456 alcSetError(dev
.get(), ALC_INVALID_VALUE
);
3459 if(al::case_compare(devname
, GetDefaultName()) == 0)
3463 /* Force the backend device to stop first since we're opening another one. */
3464 const bool wasPlaying
{dev
->mDeviceState
== DeviceState::Playing
};
3467 dev
->Backend
->stop();
3468 dev
->mDeviceState
= DeviceState::Configured
;
3471 BackendPtr newbackend
;
3473 newbackend
= PlaybackFactory
->createBackend(dev
.get(), BackendType::Playback
);
3474 newbackend
->open(devname
);
3476 catch(al::backend_exception
&e
) {
3478 newbackend
= nullptr;
3480 WARN("Failed to reopen playback device: %s\n", e
.what());
3481 alcSetError(dev
.get(), (e
.errorCode() == al::backend_error::OutOfMemory
)
3482 ? ALC_OUT_OF_MEMORY
: ALC_INVALID_VALUE
);
3484 if(dev
->Connected
.load(std::memory_order_relaxed
) && wasPlaying
)
3487 auto backend
= dev
->Backend
.get();
3489 dev
->mDeviceState
= DeviceState::Playing
;
3491 catch(al::backend_exception
&be
) {
3492 ERR("%s\n", be
.what());
3493 dev
->handleDisconnect("%s", be
.what());
3499 dev
->Backend
= std::move(newbackend
);
3500 dev
->mDeviceState
= DeviceState::Unprepared
;
3501 TRACE("Reopened device %p, \"%s\"\n", voidp
{dev
.get()}, dev
->DeviceName
.c_str());
3503 /* Always return true even if resetting fails. It shouldn't fail, but this
3504 * is primarily to avoid confusion by the app seeing the function return
3505 * false while the device is on the new output anyway. We could try to
3506 * restore the old backend if this fails, but the configuration would be
3507 * changed with the new backend and would need to be reset again with the
3508 * old one, and the provided attributes may not be appropriate or desirable
3509 * for the old device.
3511 * In this way, we essentially act as if the function succeeded, but
3512 * immediately disconnects following it.
3514 ResetDeviceParams(dev
.get(), attribs
);
3518 /************************************************
3519 * ALC event query functions
3520 ************************************************/
3522 FORCE_ALIGN ALCenum ALC_APIENTRY
alcEventIsSupportedSOFT(ALCenum eventType
, ALCenum deviceType
) noexcept
3524 auto etype
= alc::GetEventType(eventType
);
3527 WARN("Invalid event type: 0x%04x\n", eventType
);
3528 alcSetError(nullptr, ALC_INVALID_ENUM
);
3529 return ALC_EVENT_NOT_SUPPORTED_SOFT
;
3532 auto supported
= alc::EventSupport::NoSupport
;
3535 case ALC_PLAYBACK_DEVICE_SOFT
:
3537 supported
= PlaybackFactory
->queryEventSupport(*etype
, BackendType::Playback
);
3540 case ALC_CAPTURE_DEVICE_SOFT
:
3542 supported
= CaptureFactory
->queryEventSupport(*etype
, BackendType::Capture
);
3546 WARN("Invalid device type: 0x%04x\n", deviceType
);
3547 alcSetError(nullptr, ALC_INVALID_ENUM
);
3549 return al::to_underlying(supported
);