update openal

This commit is contained in:
AzaezelX 2024-06-30 14:35:57 -05:00
parent 62f3b93ff9
commit 6721a6b021
287 changed files with 33851 additions and 27325 deletions

View file

@ -4,21 +4,28 @@
#include "context.h"
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstring>
#include <functional>
#include <limits>
#include <numeric>
#include <stddef.h>
#include <stdexcept>
#include <string_view>
#include <utility>
#include "AL/efx.h"
#include "al/auxeffectslot.h"
#include "al/debug.h"
#include "al/source.h"
#include "al/effect.h"
#include "al/event.h"
#include "al/listener.h"
#include "albit.h"
#include "alc/alu.h"
#include "alc/backends/base.h"
#include "alspan.h"
#include "core/async_event.h"
#include "core/device.h"
#include "core/effectslot.h"
@ -26,63 +33,68 @@
#include "core/voice.h"
#include "core/voice_change.h"
#include "device.h"
#include "flexarray.h"
#include "ringbuffer.h"
#include "vecmat.h"
#ifdef ALSOFT_EAX
#include <cstring>
#include "alstring.h"
#include "al/eax/globals.h"
#endif // ALSOFT_EAX
namespace {
using namespace std::placeholders;
using namespace std::string_view_literals;
using voidp = void*;
/* Default context extensions */
constexpr ALchar alExtList[] =
"AL_EXT_ALAW "
"AL_EXT_BFORMAT "
"AL_EXT_DOUBLE "
"AL_EXT_EXPONENT_DISTANCE "
"AL_EXT_FLOAT32 "
"AL_EXT_IMA4 "
"AL_EXT_LINEAR_DISTANCE "
"AL_EXT_MCFORMATS "
"AL_EXT_MULAW "
"AL_EXT_MULAW_BFORMAT "
"AL_EXT_MULAW_MCFORMATS "
"AL_EXT_OFFSET "
"AL_EXT_source_distance_model "
"AL_EXT_SOURCE_RADIUS "
"AL_EXT_STATIC_BUFFER "
"AL_EXT_STEREO_ANGLES "
"AL_LOKI_quadriphonic "
"AL_SOFT_bformat_ex "
"AL_SOFTX_bformat_hoa "
"AL_SOFT_block_alignment "
"AL_SOFT_buffer_length_query "
"AL_SOFT_callback_buffer "
"AL_SOFTX_convolution_reverb "
"AL_SOFT_deferred_updates "
"AL_SOFT_direct_channels "
"AL_SOFT_direct_channels_remix "
"AL_SOFT_effect_target "
"AL_SOFT_events "
"AL_SOFT_gain_clamp_ex "
"AL_SOFTX_hold_on_disconnect "
"AL_SOFT_loop_points "
"AL_SOFTX_map_buffer "
"AL_SOFT_MSADPCM "
"AL_SOFT_source_latency "
"AL_SOFT_source_length "
"AL_SOFT_source_resampler "
"AL_SOFT_source_spatialize "
"AL_SOFT_source_start_delay "
"AL_SOFT_UHJ "
"AL_SOFT_UHJ_ex";
std::vector<std::string_view> getContextExtensions() noexcept
{
return std::vector<std::string_view>{
"AL_EXT_ALAW"sv,
"AL_EXT_BFORMAT"sv,
"AL_EXT_debug"sv,
"AL_EXT_direct_context"sv,
"AL_EXT_DOUBLE"sv,
"AL_EXT_EXPONENT_DISTANCE"sv,
"AL_EXT_FLOAT32"sv,
"AL_EXT_IMA4"sv,
"AL_EXT_LINEAR_DISTANCE"sv,
"AL_EXT_MCFORMATS"sv,
"AL_EXT_MULAW"sv,
"AL_EXT_MULAW_BFORMAT"sv,
"AL_EXT_MULAW_MCFORMATS"sv,
"AL_EXT_OFFSET"sv,
"AL_EXT_source_distance_model"sv,
"AL_EXT_SOURCE_RADIUS"sv,
"AL_EXT_STATIC_BUFFER"sv,
"AL_EXT_STEREO_ANGLES"sv,
"AL_LOKI_quadriphonic"sv,
"AL_SOFT_bformat_ex"sv,
"AL_SOFTX_bformat_hoa"sv,
"AL_SOFT_block_alignment"sv,
"AL_SOFT_buffer_length_query"sv,
"AL_SOFT_callback_buffer"sv,
"AL_SOFTX_convolution_effect"sv,
"AL_SOFT_deferred_updates"sv,
"AL_SOFT_direct_channels"sv,
"AL_SOFT_direct_channels_remix"sv,
"AL_SOFT_effect_target"sv,
"AL_SOFT_events"sv,
"AL_SOFT_gain_clamp_ex"sv,
"AL_SOFTX_hold_on_disconnect"sv,
"AL_SOFT_loop_points"sv,
"AL_SOFTX_map_buffer"sv,
"AL_SOFT_MSADPCM"sv,
"AL_SOFT_source_latency"sv,
"AL_SOFT_source_length"sv,
"AL_SOFTX_source_panning"sv,
"AL_SOFT_source_resampler"sv,
"AL_SOFT_source_spatialize"sv,
"AL_SOFT_source_start_delay"sv,
"AL_SOFT_UHJ"sv,
"AL_SOFT_UHJ_ex"sv,
};
}
} // namespace
@ -90,10 +102,9 @@ constexpr ALchar alExtList[] =
std::atomic<bool> ALCcontext::sGlobalContextLock{false};
std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
ALCcontext::ThreadCtx::~ThreadCtx()
{
if(ALCcontext *ctx{ALCcontext::sLocalContext})
if(ALCcontext *ctx{std::exchange(ALCcontext::sLocalContext, nullptr)})
{
const bool result{ctx->releaseIfNoDelete()};
ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
@ -105,23 +116,18 @@ thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
ALeffect ALCcontext::sDefaultEffect;
#ifdef __MINGW32__
ALCcontext *ALCcontext::getThreadContext() noexcept
{ return sLocalContext; }
void ALCcontext::setThreadContext(ALCcontext *context) noexcept
{ sThreadContext.set(context); }
#endif
ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
: ContextBase{device.get()}, mALDevice{std::move(device)}
ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags)
: ContextBase{device.get()}, mALDevice{std::move(device)}, mContextFlags{flags}
{
mDebugGroups.emplace_back(DebugSource::Other, 0, std::string{});
mDebugEnabled.store(mContextFlags.test(ContextFlags::DebugBit), std::memory_order_relaxed);
}
ALCcontext::~ALCcontext()
{
TRACE("Freeing context %p\n", voidp{this});
size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u},
size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), 0_uz,
[](size_t cur, const SourceSubList &sublist) noexcept -> size_t
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
if(count > 0)
@ -134,7 +140,7 @@ ALCcontext::~ALCcontext()
#endif // ALSOFT_EAX
mDefaultSlot = nullptr;
count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), 0_uz,
[](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
if(count > 0)
@ -151,16 +157,17 @@ void ALCcontext::init()
aluInitEffectPanning(mDefaultSlot->mSlot, this);
}
EffectSlotArray *auxslots;
std::unique_ptr<EffectSlotArray> auxslots;
if(!mDefaultSlot)
auxslots = EffectSlot::CreatePtrArray(0);
else
{
auxslots = EffectSlot::CreatePtrArray(1);
auxslots = EffectSlot::CreatePtrArray(2);
(*auxslots)[0] = mDefaultSlot->mSlot;
(*auxslots)[1] = mDefaultSlot->mSlot;
mDefaultSlot->mState = SlotState::Playing;
}
mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
mActiveAuxSlots.store(std::move(auxslots), std::memory_order_relaxed);
allocVoiceChanges();
{
@ -170,26 +177,41 @@ void ALCcontext::init()
mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
}
mExtensionList = alExtList;
mExtensions = getContextExtensions();
if(sBufferSubDataCompat)
{
std::string extlist{mExtensionList};
const auto pos = extlist.find("AL_EXT_SOURCE_RADIUS ");
if(pos != std::string::npos)
extlist.replace(pos, 20, "AL_SOFT_buffer_sub_data");
else
extlist += " AL_SOFT_buffer_sub_data";
mExtensionListOverride = std::move(extlist);
mExtensionList = mExtensionListOverride.c_str();
auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"sv);
if(iter != mExtensions.end()) mExtensions.erase(iter);
/* TODO: Would be nice to sort this alphabetically. Needs case-
* insensitive searching.
*/
mExtensions.emplace_back("AL_SOFT_buffer_sub_data"sv);
}
#ifdef ALSOFT_EAX
eax_initialize_extensions();
#endif // ALSOFT_EAX
if(!mExtensions.empty())
{
const size_t len{std::accumulate(mExtensions.cbegin()+1, mExtensions.cend(),
mExtensions.front().length(),
[](size_t current, std::string_view ext) noexcept
{ return current + ext.length() + 1; })};
std::string extensions;
extensions.reserve(len);
extensions += mExtensions.front();
for(std::string_view ext : al::span{mExtensions}.subspan<1>())
{
extensions += ' ';
extensions += ext;
}
mExtensionsString = std::move(extensions);
}
mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
mParams.Matrix = alu::Matrix::Identity();
mParams.Velocity = alu::Vector{};
@ -202,7 +224,7 @@ void ALCcontext::init()
mParams.mDistanceModel = mDistanceModel;
mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
mAsyncEvents = RingBuffer::Create(1024, sizeof(AsyncEvent), false);
StartEventThrd(this);
@ -210,7 +232,7 @@ void ALCcontext::init()
mActiveVoiceCount.store(64, std::memory_order_relaxed);
}
bool ALCcontext::deinit()
void ALCcontext::deinit()
{
if(sLocalContext == this)
{
@ -230,18 +252,14 @@ bool ALCcontext::deinit()
dec_ref();
}
bool ret{};
bool stopPlayback{};
/* First make sure this context exists in the device's list. */
auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
{
using ContextArray = al::FlexArray<ContextBase*>;
auto alloc_ctx_array = [](const size_t count) -> ContextArray*
{
if(count == 0) return &DeviceBase::sEmptyContextArray;
return ContextArray::Create(count).release();
};
auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
const size_t newsize{oldarray->size() - toremove};
auto newarray = ContextArray::Create(newsize);
/* Copy the current/old context handles to the new array, excluding the
* given context.
@ -252,21 +270,21 @@ bool ALCcontext::deinit()
/* Store the new context array in the device. Wait for any current mix
* to finish before deleting the old array.
*/
mDevice->mContexts.store(newarray);
if(oldarray != &DeviceBase::sEmptyContextArray)
{
mDevice->waitForMix();
delete oldarray;
}
auto prevarray = mDevice->mContexts.exchange(std::move(newarray));
std::ignore = mDevice->waitForMix();
ret = !newarray->empty();
stopPlayback = (newsize == 0);
}
else
ret = !oldarray->empty();
stopPlayback = oldarray->empty();
StopEventThrd(this);
return ret;
if(stopPlayback && mALDevice->mDeviceState == DeviceState::Playing)
{
mALDevice->Backend->stop();
mALDevice->mDeviceState = DeviceState::Configured;
}
}
void ALCcontext::applyAllUpdates()
@ -295,6 +313,7 @@ void ALCcontext::applyAllUpdates()
mHoldUpdates.store(false, std::memory_order_release);
}
#ifdef ALSOFT_EAX
namespace {
@ -306,10 +325,10 @@ void ForEachSource(ALCcontext *context, F func)
uint64_t usemask{~sublist.FreeMask};
while(usemask)
{
const int idx{al::countr_zero(usemask)};
const auto idx = static_cast<uint>(al::countr_zero(usemask));
usemask &= ~(1_u64 << idx);
func(sublist.Sources[idx]);
func((*sublist.Sources)[idx]);
}
}
}
@ -447,43 +466,15 @@ void ALCcontext::eax_initialize_extensions()
if(!eax_g_is_enabled)
return;
const auto string_max_capacity =
std::strlen(mExtensionList) + 1 +
std::strlen(eax1_ext_name) + 1 +
std::strlen(eax2_ext_name) + 1 +
std::strlen(eax3_ext_name) + 1 +
std::strlen(eax4_ext_name) + 1 +
std::strlen(eax5_ext_name) + 1 +
std::strlen(eax_x_ram_ext_name) + 1;
std::string extlist;
extlist.reserve(string_max_capacity);
mExtensions.emplace(mExtensions.begin(), "EAX-RAM"sv);
if(eaxIsCapable())
{
extlist += eax1_ext_name;
extlist += ' ';
extlist += eax2_ext_name;
extlist += ' ';
extlist += eax3_ext_name;
extlist += ' ';
extlist += eax4_ext_name;
extlist += ' ';
extlist += eax5_ext_name;
extlist += ' ';
mExtensions.emplace(mExtensions.begin(), "EAX5.0"sv);
mExtensions.emplace(mExtensions.begin(), "EAX4.0"sv);
mExtensions.emplace(mExtensions.begin(), "EAX3.0"sv);
mExtensions.emplace(mExtensions.begin(), "EAX2.0"sv);
mExtensions.emplace(mExtensions.begin(), "EAX"sv);
}
extlist += eax_x_ram_ext_name;
extlist += ' ';
extlist += mExtensionList;
mExtensionListOverride = std::move(extlist);
mExtensionList = mExtensionListOverride.c_str();
}
void ALCcontext::eax_initialize()
@ -555,10 +546,11 @@ unsigned long ALCcontext::eax_detect_speaker_configuration() const
case DevFmtX51: return SPEAKERS_5;
case DevFmtX61: return SPEAKERS_6;
case DevFmtX71: return SPEAKERS_7;
/* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
/* 7.1.4(.4) is compatible with 7.1. This could instead be HEADPHONES to
* suggest with-height surround sound (like HRTF).
*/
case DevFmtX714: return SPEAKERS_7;
case DevFmtX7144: return SPEAKERS_7;
/* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
* suggest full-sphere surround sound (like HRTF).
*/
@ -582,7 +574,7 @@ void ALCcontext::eax_update_speaker_configuration()
void ALCcontext::eax_set_last_error_defaults() noexcept
{
mEaxLastError = EAX_OK;
mEaxLastError = EAXCONTEXT_DEFAULTLASTERROR;
}
void ALCcontext::eax_session_set_defaults() noexcept
@ -671,6 +663,7 @@ void ALCcontext::eax_get_misc(const EaxCall& call)
break;
case EAXCONTEXT_LASTERROR:
call.set_value<ContextException>(mEaxLastError);
mEaxLastError = EAX_OK;
break;
case EAXCONTEXT_SPEAKERCONFIG:
call.set_value<ContextException>(mEaxSpeakerConfig);
@ -1018,88 +1011,49 @@ void ALCcontext::eaxCommit()
eax_update_sources();
}
namespace {
class EaxSetException : public EaxException {
public:
explicit EaxSetException(const char* message)
: EaxException{"EAX_SET", message}
{}
};
[[noreturn]] void eax_fail_set(const char* message)
{
throw EaxSetException{message};
}
class EaxGetException : public EaxException {
public:
explicit EaxGetException(const char* message)
: EaxException{"EAX_GET", message}
{}
};
[[noreturn]] void eax_fail_get(const char* message)
{
throw EaxGetException{message};
}
} // namespace
FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
const GUID* property_set_id,
ALuint property_id,
ALuint property_source_id,
ALvoid* property_value,
ALuint property_value_size) noexcept
try
FORCE_ALIGN auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
{
auto context = GetContextRef();
if(!context)
eax_fail_set("No current context.");
std::lock_guard<std::mutex> prop_lock{context->mPropLock};
return context->eax_eax_set(
property_set_id,
property_id,
property_source_id,
property_value,
property_value_size);
if(!context) UNLIKELY return AL_INVALID_OPERATION;
return EAXSetDirect(context.get(), property_set_id, property_id, source_id, value, value_size);
}
catch (...)
FORCE_ALIGN auto AL_APIENTRY EAXSetDirect(ALCcontext *context, const GUID *property_set_id,
ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
try
{
eax_log_exception(__func__);
std::lock_guard<std::mutex> prop_lock{context->mPropLock};
return context->eax_eax_set(property_set_id, property_id, source_id, value, value_size);
}
catch(...)
{
context->eaxSetLastError();
eax_log_exception(std::data(__func__));
return AL_INVALID_OPERATION;
}
FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
const GUID* property_set_id,
ALuint property_id,
ALuint property_source_id,
ALvoid* property_value,
ALuint property_value_size) noexcept
try
FORCE_ALIGN auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id,
ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
{
auto context = GetContextRef();
if(!context)
eax_fail_get("No current context.");
std::lock_guard<std::mutex> prop_lock{context->mPropLock};
return context->eax_eax_get(
property_set_id,
property_id,
property_source_id,
property_value,
property_value_size);
if(!context) UNLIKELY return AL_INVALID_OPERATION;
return EAXGetDirect(context.get(), property_set_id, property_id, source_id, value, value_size);
}
catch (...)
FORCE_ALIGN auto AL_APIENTRY EAXGetDirect(ALCcontext *context, const GUID *property_set_id,
ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum
try
{
eax_log_exception(__func__);
std::lock_guard<std::mutex> prop_lock{context->mPropLock};
return context->eax_eax_get(property_set_id, property_id, source_id, value, value_size);
}
catch(...)
{
context->eaxSetLastError();
eax_log_exception(std::data(__func__));
return AL_INVALID_OPERATION;
}
#endif // ALSOFT_EAX