2021-04-27 08:04:09 -07:00
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
2022-12-16 18:41:47 -08:00
|
|
|
#include <cassert>
|
2023-12-08 11:53:51 -08:00
|
|
|
#include <functional>
|
2023-05-12 16:01:06 -07:00
|
|
|
#include <limits>
|
2021-12-17 04:25:46 -08:00
|
|
|
#include <memory>
|
2023-05-12 16:01:06 -07:00
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <utility>
|
2021-04-27 08:04:09 -07:00
|
|
|
|
2021-12-17 17:34:03 -08:00
|
|
|
#include "async_event.h"
|
2021-12-17 04:25:46 -08:00
|
|
|
#include "context.h"
|
|
|
|
|
#include "device.h"
|
2021-12-17 17:34:03 -08:00
|
|
|
#include "effectslot.h"
|
2021-12-17 04:25:46 -08:00
|
|
|
#include "logging.h"
|
2021-12-17 17:34:03 -08:00
|
|
|
#include "ringbuffer.h"
|
2021-12-17 04:25:46 -08:00
|
|
|
#include "voice.h"
|
|
|
|
|
#include "voice_change.h"
|
|
|
|
|
|
|
|
|
|
|
2022-12-16 18:41:47 -08:00
|
|
|
#ifdef __cpp_lib_atomic_is_always_lock_free
|
|
|
|
|
static_assert(std::atomic<ContextBase::AsyncEventBitset>::is_always_lock_free, "atomic<bitset> isn't lock-free");
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-12-17 17:34:03 -08:00
|
|
|
ContextBase::ContextBase(DeviceBase *device) : mDevice{device}
|
2022-12-16 18:41:47 -08:00
|
|
|
{ assert(mEnabledEvts.is_lock_free()); }
|
2021-12-17 17:34:03 -08:00
|
|
|
|
|
|
|
|
ContextBase::~ContextBase()
|
|
|
|
|
{
|
2024-03-21 12:54:52 -07:00
|
|
|
mActiveAuxSlots.store(nullptr, std::memory_order_relaxed);
|
2024-01-01 10:53:52 -08:00
|
|
|
mVoices.store(nullptr, std::memory_order_relaxed);
|
2021-12-17 17:34:03 -08:00
|
|
|
|
|
|
|
|
if(mAsyncEvents)
|
|
|
|
|
{
|
2024-01-03 14:12:28 -08:00
|
|
|
size_t count{0};
|
2021-12-17 17:34:03 -08:00
|
|
|
auto evt_vec = mAsyncEvents->getReadVector();
|
|
|
|
|
if(evt_vec.first.len > 0)
|
|
|
|
|
{
|
2023-05-24 11:56:56 -07:00
|
|
|
std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf)),
|
|
|
|
|
evt_vec.first.len);
|
2021-12-17 17:34:03 -08:00
|
|
|
count += evt_vec.first.len;
|
|
|
|
|
}
|
|
|
|
|
if(evt_vec.second.len > 0)
|
|
|
|
|
{
|
2023-05-24 11:56:56 -07:00
|
|
|
std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf)),
|
|
|
|
|
evt_vec.second.len);
|
2021-12-17 17:34:03 -08:00
|
|
|
count += evt_vec.second.len;
|
|
|
|
|
}
|
|
|
|
|
if(count > 0)
|
|
|
|
|
TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s");
|
|
|
|
|
mAsyncEvents->readAdvance(count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-12-17 04:25:46 -08:00
|
|
|
void ContextBase::allocVoiceChanges()
|
|
|
|
|
{
|
2023-12-08 12:23:54 -08:00
|
|
|
static constexpr size_t clustersize{std::tuple_size_v<VoiceChangeCluster::element_type>};
|
|
|
|
|
|
2023-12-08 11:53:51 -08:00
|
|
|
VoiceChangeCluster clusterptr{std::make_unique<VoiceChangeCluster::element_type>()};
|
|
|
|
|
const auto cluster = al::span{*clusterptr};
|
2021-12-17 04:25:46 -08:00
|
|
|
|
|
|
|
|
for(size_t i{1};i < clustersize;++i)
|
|
|
|
|
cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed);
|
|
|
|
|
cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed);
|
|
|
|
|
|
2023-12-08 11:53:51 -08:00
|
|
|
mVoiceChangeClusters.emplace_back(std::move(clusterptr));
|
|
|
|
|
mVoiceChangeTail = mVoiceChangeClusters.back()->data();
|
2021-12-17 04:25:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ContextBase::allocVoiceProps()
|
|
|
|
|
{
|
2023-12-08 12:23:54 -08:00
|
|
|
static constexpr size_t clustersize{std::tuple_size_v<VoicePropsCluster::element_type>};
|
2021-12-17 04:25:46 -08:00
|
|
|
|
|
|
|
|
TRACE("Increasing allocated voice properties to %zu\n",
|
|
|
|
|
(mVoicePropClusters.size()+1) * clustersize);
|
|
|
|
|
|
2023-12-08 12:23:54 -08:00
|
|
|
auto clusterptr = std::make_unique<VoicePropsCluster::element_type>();
|
2023-12-08 11:53:51 -08:00
|
|
|
auto cluster = al::span{*clusterptr};
|
2021-12-17 04:25:46 -08:00
|
|
|
for(size_t i{1};i < clustersize;++i)
|
|
|
|
|
cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed);
|
2023-12-08 11:53:51 -08:00
|
|
|
mVoicePropClusters.emplace_back(std::move(clusterptr));
|
2021-12-17 04:25:46 -08:00
|
|
|
|
|
|
|
|
VoicePropsItem *oldhead{mFreeVoiceProps.load(std::memory_order_acquire)};
|
|
|
|
|
do {
|
2023-12-08 11:53:51 -08:00
|
|
|
mVoicePropClusters.back()->back().next.store(oldhead, std::memory_order_relaxed);
|
|
|
|
|
} while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back()->data(),
|
2021-12-17 04:25:46 -08:00
|
|
|
std::memory_order_acq_rel, std::memory_order_acquire) == false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ContextBase::allocVoices(size_t addcount)
|
|
|
|
|
{
|
2023-12-08 12:23:54 -08:00
|
|
|
static constexpr size_t clustersize{std::tuple_size_v<VoiceCluster::element_type>};
|
|
|
|
|
/* Convert element count to cluster count. */
|
|
|
|
|
addcount = (addcount+(clustersize-1)) / clustersize;
|
|
|
|
|
|
2023-12-08 11:53:51 -08:00
|
|
|
if(!addcount)
|
|
|
|
|
{
|
|
|
|
|
if(!mVoiceClusters.empty())
|
|
|
|
|
return;
|
|
|
|
|
++addcount;
|
|
|
|
|
}
|
2021-12-17 04:25:46 -08:00
|
|
|
|
2023-12-08 12:23:54 -08:00
|
|
|
if(addcount >= std::numeric_limits<int>::max()/clustersize - mVoiceClusters.size())
|
|
|
|
|
throw std::runtime_error{"Allocating too many voices"};
|
|
|
|
|
const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize};
|
|
|
|
|
TRACE("Increasing allocated voices to %zu\n", totalcount);
|
|
|
|
|
|
2023-12-08 11:53:51 -08:00
|
|
|
while(addcount)
|
|
|
|
|
{
|
2023-12-08 12:23:54 -08:00
|
|
|
mVoiceClusters.emplace_back(std::make_unique<VoiceCluster::element_type>());
|
|
|
|
|
--addcount;
|
2023-12-08 11:53:51 -08:00
|
|
|
}
|
|
|
|
|
|
2021-12-17 04:25:46 -08:00
|
|
|
auto newarray = VoiceArray::Create(totalcount);
|
|
|
|
|
auto voice_iter = newarray->begin();
|
|
|
|
|
for(VoiceCluster &cluster : mVoiceClusters)
|
2023-12-08 11:53:51 -08:00
|
|
|
voice_iter = std::transform(cluster->begin(), cluster->end(), voice_iter,
|
|
|
|
|
[](Voice &voice) noexcept -> Voice* { return &voice; });
|
2021-12-17 04:25:46 -08:00
|
|
|
|
2024-01-01 10:53:52 -08:00
|
|
|
if(auto oldvoices = mVoices.exchange(std::move(newarray), std::memory_order_acq_rel))
|
2023-12-08 10:11:08 -08:00
|
|
|
std::ignore = mDevice->waitForMix();
|
2021-12-17 04:25:46 -08:00
|
|
|
}
|
2022-07-15 06:14:25 -07:00
|
|
|
|
|
|
|
|
|
2024-01-03 13:16:09 -08:00
|
|
|
void ContextBase::allocEffectSlotProps()
|
|
|
|
|
{
|
|
|
|
|
static constexpr size_t clustersize{std::tuple_size_v<EffectSlotPropsCluster::element_type>};
|
|
|
|
|
|
|
|
|
|
TRACE("Increasing allocated effect slot properties to %zu\n",
|
|
|
|
|
(mEffectSlotPropClusters.size()+1) * clustersize);
|
|
|
|
|
|
|
|
|
|
auto clusterptr = std::make_unique<EffectSlotPropsCluster::element_type>();
|
|
|
|
|
auto cluster = al::span{*clusterptr};
|
|
|
|
|
for(size_t i{1};i < clustersize;++i)
|
|
|
|
|
cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed);
|
|
|
|
|
auto *newcluster = mEffectSlotPropClusters.emplace_back(std::move(clusterptr)).get();
|
|
|
|
|
|
|
|
|
|
EffectSlotProps *oldhead{mFreeEffectSlotProps.load(std::memory_order_acquire)};
|
|
|
|
|
do {
|
|
|
|
|
newcluster->back().next.store(oldhead, std::memory_order_relaxed);
|
|
|
|
|
} while(mFreeEffectSlotProps.compare_exchange_weak(oldhead, newcluster->data(),
|
|
|
|
|
std::memory_order_acq_rel, std::memory_order_acquire) == false);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-15 06:14:25 -07:00
|
|
|
EffectSlot *ContextBase::getEffectSlot()
|
|
|
|
|
{
|
2023-12-08 11:53:51 -08:00
|
|
|
for(auto& clusterptr : mEffectSlotClusters)
|
2022-07-15 06:14:25 -07:00
|
|
|
{
|
2023-12-08 11:53:51 -08:00
|
|
|
const auto cluster = al::span{*clusterptr};
|
|
|
|
|
auto iter = std::find_if_not(cluster.begin(), cluster.end(),
|
|
|
|
|
std::mem_fn(&EffectSlot::InUse));
|
|
|
|
|
if(iter != cluster.end()) return al::to_address(iter);
|
2022-07-15 06:14:25 -07:00
|
|
|
}
|
|
|
|
|
|
2023-12-08 11:53:51 -08:00
|
|
|
auto clusterptr = std::make_unique<EffectSlotCluster::element_type>();
|
|
|
|
|
if(1 >= std::numeric_limits<int>::max()/clusterptr->size() - mEffectSlotClusters.size())
|
2022-07-15 06:14:25 -07:00
|
|
|
throw std::runtime_error{"Allocating too many effect slots"};
|
2023-12-08 11:53:51 -08:00
|
|
|
const size_t totalcount{(mEffectSlotClusters.size()+1) * clusterptr->size()};
|
2022-07-15 06:14:25 -07:00
|
|
|
TRACE("Increasing allocated effect slots to %zu\n", totalcount);
|
|
|
|
|
|
2023-12-08 11:53:51 -08:00
|
|
|
mEffectSlotClusters.emplace_back(std::move(clusterptr));
|
|
|
|
|
return mEffectSlotClusters.back()->data();
|
2022-07-15 06:14:25 -07:00
|
|
|
}
|
2024-01-03 14:12:28 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
void ContextBase::allocContextProps()
|
|
|
|
|
{
|
|
|
|
|
static constexpr size_t clustersize{std::tuple_size_v<ContextPropsCluster::element_type>};
|
|
|
|
|
|
|
|
|
|
TRACE("Increasing allocated context properties to %zu\n",
|
|
|
|
|
(mContextPropClusters.size()+1) * clustersize);
|
|
|
|
|
|
|
|
|
|
auto clusterptr = std::make_unique<ContextPropsCluster::element_type>();
|
|
|
|
|
auto cluster = al::span{*clusterptr};
|
|
|
|
|
for(size_t i{1};i < clustersize;++i)
|
|
|
|
|
cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed);
|
|
|
|
|
auto *newcluster = mContextPropClusters.emplace_back(std::move(clusterptr)).get();
|
|
|
|
|
|
|
|
|
|
ContextProps *oldhead{mFreeContextProps.load(std::memory_order_acquire)};
|
|
|
|
|
do {
|
|
|
|
|
newcluster->back().next.store(oldhead, std::memory_order_relaxed);
|
|
|
|
|
} while(mFreeContextProps.compare_exchange_weak(oldhead, newcluster->data(),
|
|
|
|
|
std::memory_order_acq_rel, std::memory_order_acquire) == false);
|
|
|
|
|
}
|