mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-16 00:54:54 +00:00
* BugFix: Correct convexDecomp compilation by setting the LINUX flag when necessary.
* BugFix: Update OpenAL to correct a compilation error on Linux.
This commit is contained in:
parent
e071f1d901
commit
7380161054
234 changed files with 30864 additions and 7523 deletions
|
|
@ -1,11 +1,11 @@
|
|||
#ifndef AL_BIT_H
|
||||
#define AL_BIT_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#if !defined(__GNUC__) && (defined(_WIN32) || defined(_WIN64))
|
||||
#include <intrin.h>
|
||||
#include "opthelpers.h"
|
||||
#endif
|
||||
|
||||
namespace al {
|
||||
|
|
@ -21,19 +21,19 @@ enum class endian {
|
|||
|
||||
/* This doesn't support mixed-endian. */
|
||||
namespace detail_ {
|
||||
constexpr inline bool EndianTest() noexcept
|
||||
constexpr bool IsLittleEndian() noexcept
|
||||
{
|
||||
static_assert(sizeof(char) < sizeof(int), "char is too big");
|
||||
|
||||
constexpr int test_val{1};
|
||||
return static_cast<const char&>(test_val);
|
||||
return static_cast<const char&>(test_val) ? true : false;
|
||||
}
|
||||
} // namespace detail_
|
||||
|
||||
enum class endian {
|
||||
little = 0,
|
||||
big = 1,
|
||||
native = detail_::EndianTest() ? little : big
|
||||
big = 0,
|
||||
little = 1,
|
||||
native = detail_::IsLittleEndian() ? little : big
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
@ -73,6 +73,17 @@ int> countr_zero(T val) noexcept
|
|||
* they're good enough if the GCC built-ins aren't available.
|
||||
*/
|
||||
namespace detail_ {
|
||||
template<typename T, size_t = std::numeric_limits<T>::digits>
|
||||
struct fast_utype { };
|
||||
template<typename T>
|
||||
struct fast_utype<T,8> { using type = std::uint_fast8_t; };
|
||||
template<typename T>
|
||||
struct fast_utype<T,16> { using type = std::uint_fast16_t; };
|
||||
template<typename T>
|
||||
struct fast_utype<T,32> { using type = std::uint_fast32_t; };
|
||||
template<typename T>
|
||||
struct fast_utype<T,64> { using type = std::uint_fast64_t; };
|
||||
|
||||
template<typename T>
|
||||
constexpr T repbits(unsigned char bits) noexcept
|
||||
{
|
||||
|
|
@ -85,47 +96,47 @@ namespace detail_ {
|
|||
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
||||
int> popcount(T v) noexcept
|
||||
int> popcount(T val) noexcept
|
||||
{
|
||||
constexpr T m55{detail_::repbits<T>(0x55)};
|
||||
constexpr T m33{detail_::repbits<T>(0x33)};
|
||||
constexpr T m0f{detail_::repbits<T>(0x0f)};
|
||||
constexpr T m01{detail_::repbits<T>(0x01)};
|
||||
using fast_type = typename detail_::fast_utype<T>::type;
|
||||
constexpr fast_type b01010101{detail_::repbits<fast_type>(0x55)};
|
||||
constexpr fast_type b00110011{detail_::repbits<fast_type>(0x33)};
|
||||
constexpr fast_type b00001111{detail_::repbits<fast_type>(0x0f)};
|
||||
constexpr fast_type b00000001{detail_::repbits<fast_type>(0x01)};
|
||||
|
||||
v = v - ((v >> 1) & m55);
|
||||
v = (v & m33) + ((v >> 2) & m33);
|
||||
v = (v + (v >> 4)) & m0f;
|
||||
return static_cast<int>((v * m01) >> ((sizeof(T)-1)*8));
|
||||
fast_type v{fast_type{val} - ((fast_type{val} >> 1) & b01010101)};
|
||||
v = (v & b00110011) + ((v >> 2) & b00110011);
|
||||
v = (v + (v >> 4)) & b00001111;
|
||||
return static_cast<int>(((v * b00000001) >> ((sizeof(T)-1)*8)) & 0xff);
|
||||
}
|
||||
|
||||
#if defined(_WIN64)
|
||||
#ifdef _WIN32
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
||||
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value
|
||||
&& std::numeric_limits<T>::digits <= 32,
|
||||
int> countr_zero(T v)
|
||||
{
|
||||
unsigned long idx{std::numeric_limits<T>::digits};
|
||||
if_constexpr(std::numeric_limits<T>::digits <= 32)
|
||||
_BitScanForward(&idx, static_cast<uint32_t>(v));
|
||||
else // std::numeric_limits<T>::digits > 32
|
||||
_BitScanForward64(&idx, v);
|
||||
_BitScanForward(&idx, static_cast<uint32_t>(v));
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
||||
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value
|
||||
&& 32 < std::numeric_limits<T>::digits && std::numeric_limits<T>::digits <= 64,
|
||||
int> countr_zero(T v)
|
||||
{
|
||||
unsigned long idx{std::numeric_limits<T>::digits};
|
||||
if_constexpr(std::numeric_limits<T>::digits <= 32)
|
||||
_BitScanForward(&idx, static_cast<uint32_t>(v));
|
||||
else if(!_BitScanForward(&idx, static_cast<uint32_t>(v)))
|
||||
#ifdef _WIN64
|
||||
_BitScanForward64(&idx, v);
|
||||
#else
|
||||
if(!_BitScanForward(&idx, static_cast<uint32_t>(v)))
|
||||
{
|
||||
if(_BitScanForward(&idx, static_cast<uint32_t>(v>>32)))
|
||||
idx += 32;
|
||||
}
|
||||
#endif /* _WIN64 */
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,57 +10,7 @@ using uint = unsigned int;
|
|||
|
||||
namespace al {
|
||||
|
||||
/* The "canonical" way to store raw byte data. Like C++17's std::byte, it's not
|
||||
* treated as a character type and does not work with arithmatic ops. Only
|
||||
* bitwise ops are allowed.
|
||||
*/
|
||||
enum class byte : unsigned char { };
|
||||
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value,T>
|
||||
to_integer(al::byte b) noexcept { return T(b); }
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value,al::byte>
|
||||
operator<<(al::byte lhs, T rhs) noexcept { return al::byte(to_integer<uint>(lhs) << rhs); }
|
||||
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value,al::byte>
|
||||
operator>>(al::byte lhs, T rhs) noexcept { return al::byte(to_integer<uint>(lhs) >> rhs); }
|
||||
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value,al::byte&>
|
||||
operator<<=(al::byte &lhs, T rhs) noexcept { lhs = lhs << rhs; return lhs; }
|
||||
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value,al::byte&>
|
||||
operator>>=(al::byte &lhs, T rhs) noexcept { lhs = lhs >> rhs; return lhs; }
|
||||
|
||||
#define AL_DECL_OP(op, opeq) \
|
||||
template<typename T> \
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value,al::byte> \
|
||||
operator op (al::byte lhs, T rhs) noexcept \
|
||||
{ return al::byte(to_integer<uint>(lhs) op static_cast<uint>(rhs)); } \
|
||||
\
|
||||
template<typename T> \
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value,al::byte&> \
|
||||
operator opeq (al::byte &lhs, T rhs) noexcept { lhs = lhs op rhs; return lhs; } \
|
||||
\
|
||||
constexpr al::byte operator op (al::byte lhs, al::byte rhs) noexcept \
|
||||
{ return al::byte(lhs op to_integer<uint>(rhs)); } \
|
||||
\
|
||||
constexpr al::byte& operator opeq (al::byte &lhs, al::byte rhs) noexcept \
|
||||
{ lhs = lhs op rhs; return lhs; }
|
||||
|
||||
AL_DECL_OP(|, |=)
|
||||
AL_DECL_OP(&, &=)
|
||||
AL_DECL_OP(^, ^=)
|
||||
|
||||
#undef AL_DECL_OP
|
||||
|
||||
constexpr al::byte operator~(al::byte b) noexcept
|
||||
{ return al::byte(~to_integer<uint>(b)); }
|
||||
using byte = unsigned char;
|
||||
|
||||
} // namespace al
|
||||
|
||||
|
|
|
|||
|
|
@ -4,15 +4,95 @@
|
|||
#include "alcomplex.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include "albit.h"
|
||||
#include "alnumbers.h"
|
||||
#include "alnumeric.h"
|
||||
#include "math_defs.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using ushort = unsigned short;
|
||||
using ushort2 = std::pair<ushort,ushort>;
|
||||
|
||||
/* Because std::array doesn't have constexpr non-const accessors in C++14. */
|
||||
template<typename T, size_t N>
|
||||
struct our_array {
|
||||
T mData[N];
|
||||
};
|
||||
|
||||
constexpr size_t BitReverseCounter(size_t log2_size) noexcept
|
||||
{
|
||||
/* Some magic math that calculates the number of swaps needed for a
|
||||
* sequence of bit-reversed indices when index < reversed_index.
|
||||
*/
|
||||
return (1u<<(log2_size-1)) - (1u<<((log2_size-1u)/2u));
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
constexpr auto GetBitReverser() noexcept
|
||||
{
|
||||
static_assert(N <= sizeof(ushort)*8, "Too many bits for the bit-reversal table.");
|
||||
|
||||
our_array<ushort2, BitReverseCounter(N)> ret{};
|
||||
const size_t fftsize{1u << N};
|
||||
size_t ret_i{0};
|
||||
|
||||
/* Bit-reversal permutation applied to a sequence of fftsize items. */
|
||||
for(size_t idx{1u};idx < fftsize-1;++idx)
|
||||
{
|
||||
size_t revidx{0u}, imask{idx};
|
||||
for(size_t i{0};i < N;++i)
|
||||
{
|
||||
revidx = (revidx<<1) | (imask&1);
|
||||
imask >>= 1;
|
||||
}
|
||||
|
||||
if(idx < revidx)
|
||||
{
|
||||
ret.mData[ret_i].first = static_cast<ushort>(idx);
|
||||
ret.mData[ret_i].second = static_cast<ushort>(revidx);
|
||||
++ret_i;
|
||||
}
|
||||
}
|
||||
assert(ret_i == al::size(ret.mData));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* These bit-reversal swap tables support up to 10-bit indices (1024 elements),
|
||||
* which is the largest used by OpenAL Soft's filters and effects. Larger FFT
|
||||
* requests, used by some utilities where performance is less important, will
|
||||
* use a slower table-less path.
|
||||
*/
|
||||
constexpr auto BitReverser2 = GetBitReverser<2>();
|
||||
constexpr auto BitReverser3 = GetBitReverser<3>();
|
||||
constexpr auto BitReverser4 = GetBitReverser<4>();
|
||||
constexpr auto BitReverser5 = GetBitReverser<5>();
|
||||
constexpr auto BitReverser6 = GetBitReverser<6>();
|
||||
constexpr auto BitReverser7 = GetBitReverser<7>();
|
||||
constexpr auto BitReverser8 = GetBitReverser<8>();
|
||||
constexpr auto BitReverser9 = GetBitReverser<9>();
|
||||
constexpr auto BitReverser10 = GetBitReverser<10>();
|
||||
constexpr al::span<const ushort2> gBitReverses[11]{
|
||||
{}, {},
|
||||
BitReverser2.mData,
|
||||
BitReverser3.mData,
|
||||
BitReverser4.mData,
|
||||
BitReverser5.mData,
|
||||
BitReverser6.mData,
|
||||
BitReverser7.mData,
|
||||
BitReverser8.mData,
|
||||
BitReverser9.mData,
|
||||
BitReverser10.mData
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void complex_fft(const al::span<std::complex<double>> buffer, const double sign)
|
||||
{
|
||||
const size_t fftsize{buffer.size()};
|
||||
|
|
@ -21,27 +101,33 @@ void complex_fft(const al::span<std::complex<double>> buffer, const double sign)
|
|||
*/
|
||||
const size_t log2_size{static_cast<size_t>(al::countr_zero(fftsize))};
|
||||
|
||||
/* Bit-reversal permutation applied to a sequence of fftsize items. */
|
||||
for(size_t idx{1u};idx < fftsize-1;++idx)
|
||||
if(unlikely(log2_size >= al::size(gBitReverses)))
|
||||
{
|
||||
size_t revidx{0u}, imask{idx};
|
||||
for(size_t i{0};i < log2_size;++i)
|
||||
for(size_t idx{1u};idx < fftsize-1;++idx)
|
||||
{
|
||||
revidx = (revidx<<1) | (imask&1);
|
||||
imask >>= 1;
|
||||
}
|
||||
size_t revidx{0u}, imask{idx};
|
||||
for(size_t i{0};i < log2_size;++i)
|
||||
{
|
||||
revidx = (revidx<<1) | (imask&1);
|
||||
imask >>= 1;
|
||||
}
|
||||
|
||||
if(idx < revidx)
|
||||
std::swap(buffer[idx], buffer[revidx]);
|
||||
if(idx < revidx)
|
||||
std::swap(buffer[idx], buffer[revidx]);
|
||||
}
|
||||
}
|
||||
else for(auto &rev : gBitReverses[log2_size])
|
||||
std::swap(buffer[rev.first], buffer[rev.second]);
|
||||
|
||||
/* Iterative form of Danielson-Lanczos lemma */
|
||||
const double pi{al::numbers::pi * sign};
|
||||
size_t step2{1u};
|
||||
for(size_t i{0};i < log2_size;++i)
|
||||
{
|
||||
const double arg{al::MathDefs<double>::Pi() / static_cast<double>(step2)};
|
||||
const double arg{pi / static_cast<double>(step2)};
|
||||
|
||||
const std::complex<double> w{std::cos(arg), std::sin(arg)*sign};
|
||||
/* TODO: Would std::polar(1.0, arg) be any better? */
|
||||
const std::complex<double> w{std::cos(arg), std::sin(arg)};
|
||||
std::complex<double> u{1.0, 0.0};
|
||||
const size_t step{step2 << 1};
|
||||
for(size_t j{0};j < step2;j++)
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@
|
|||
#include "pragmadefs.h"
|
||||
|
||||
|
||||
[[gnu::alloc_align(1), gnu::alloc_size(2)]] void *al_malloc(size_t alignment, size_t size);
|
||||
[[gnu::alloc_align(1), gnu::alloc_size(2)]] void *al_calloc(size_t alignment, size_t size);
|
||||
void al_free(void *ptr) noexcept;
|
||||
[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]]
|
||||
void *al_malloc(size_t alignment, size_t size);
|
||||
[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]]
|
||||
void *al_calloc(size_t alignment, size_t size);
|
||||
|
||||
|
||||
#define DISABLE_ALLOC() \
|
||||
|
|
@ -48,8 +50,8 @@ enum FamCount : size_t { };
|
|||
#define DEF_FAM_NEWDEL(T, FamMem) \
|
||||
static constexpr size_t Sizeof(size_t count) noexcept \
|
||||
{ \
|
||||
return std::max<size_t>(sizeof(T), \
|
||||
decltype(FamMem)::Sizeof(count, offsetof(T, FamMem))); \
|
||||
return std::max(decltype(FamMem)::Sizeof(count, offsetof(T, FamMem)), \
|
||||
sizeof(T)); \
|
||||
} \
|
||||
\
|
||||
void *operator new(size_t /*size*/, FamCount count) \
|
||||
|
|
@ -95,12 +97,15 @@ struct allocator {
|
|||
void deallocate(T *p, std::size_t) noexcept { al_free(p); }
|
||||
};
|
||||
template<typename T, std::size_t N, typename U, std::size_t M>
|
||||
bool operator==(const allocator<T,N>&, const allocator<U,M>&) noexcept { return true; }
|
||||
constexpr bool operator==(const allocator<T,N>&, const allocator<U,M>&) noexcept { return true; }
|
||||
template<typename T, std::size_t N, typename U, std::size_t M>
|
||||
bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept { return false; }
|
||||
constexpr bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept { return false; }
|
||||
|
||||
template<size_t alignment, typename T>
|
||||
[[gnu::assume_aligned(alignment)]] inline T* assume_aligned(T *ptr) noexcept { return ptr; }
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
constexpr T* construct_at(T *ptr, Args&& ...args)
|
||||
noexcept(std::is_nothrow_constructible<T, Args...>::value)
|
||||
{ return ::new(static_cast<void*>(ptr)) T{std::forward<Args>(args)...}; }
|
||||
|
||||
/* At least VS 2015 complains that 'ptr' is unused when the given type's
|
||||
* destructor is trivial (a no-op). So disable that warning for this call.
|
||||
|
|
@ -114,14 +119,14 @@ destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<T>::value)
|
|||
DIAGNOSTIC_POP
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_array<T>::value>
|
||||
destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<T>::value)
|
||||
destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<std::remove_all_extents_t<T>>::value)
|
||||
{
|
||||
for(auto &elem : *ptr)
|
||||
al::destroy_at(std::addressof(elem));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void destroy(T first, T end)
|
||||
constexpr void destroy(T first, T end) noexcept(noexcept(al::destroy_at(std::addressof(*first))))
|
||||
{
|
||||
while(first != end)
|
||||
{
|
||||
|
|
@ -132,7 +137,7 @@ constexpr void destroy(T first, T end)
|
|||
|
||||
template<typename T, typename N>
|
||||
constexpr std::enable_if_t<std::is_integral<N>::value,T>
|
||||
destroy_n(T first, N count)
|
||||
destroy_n(T first, N count) noexcept(noexcept(al::destroy_at(std::addressof(*first))))
|
||||
{
|
||||
if(count != 0)
|
||||
{
|
||||
|
|
@ -146,8 +151,8 @@ destroy_n(T first, N count)
|
|||
|
||||
|
||||
template<typename T, typename N>
|
||||
inline std::enable_if_t<std::is_integral<N>::value,T>
|
||||
uninitialized_default_construct_n(T first, N count)
|
||||
inline std::enable_if_t<std::is_integral<N>::value,
|
||||
T> uninitialized_default_construct_n(T first, N count)
|
||||
{
|
||||
using ValueT = typename std::iterator_traits<T>::value_type;
|
||||
T current{first};
|
||||
|
|
@ -172,10 +177,7 @@ uninitialized_default_construct_n(T first, N count)
|
|||
* trivially destructible.
|
||||
*/
|
||||
template<typename T, size_t alignment, bool = std::is_trivially_destructible<T>::value>
|
||||
struct FlexArrayStorage;
|
||||
|
||||
template<typename T, size_t alignment>
|
||||
struct FlexArrayStorage<T,alignment,true> {
|
||||
struct FlexArrayStorage {
|
||||
const size_t mSize;
|
||||
union {
|
||||
char mDummy;
|
||||
|
|
@ -184,8 +186,8 @@ struct FlexArrayStorage<T,alignment,true> {
|
|||
|
||||
static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept
|
||||
{
|
||||
return std::max<size_t>(offsetof(FlexArrayStorage, mArray) + sizeof(T)*count,
|
||||
sizeof(FlexArrayStorage)) + base;
|
||||
const size_t len{sizeof(T)*count};
|
||||
return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base;
|
||||
}
|
||||
|
||||
FlexArrayStorage(size_t size) : mSize{size}
|
||||
|
|
@ -206,8 +208,8 @@ struct FlexArrayStorage<T,alignment,false> {
|
|||
|
||||
static constexpr size_t Sizeof(size_t count, size_t base) noexcept
|
||||
{
|
||||
return std::max<size_t>(offsetof(FlexArrayStorage, mArray) + sizeof(T)*count,
|
||||
sizeof(FlexArrayStorage)) + base;
|
||||
const size_t len{sizeof(T)*count};
|
||||
return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base;
|
||||
}
|
||||
|
||||
FlexArrayStorage(size_t size) : mSize{size}
|
||||
|
|
@ -248,7 +250,7 @@ struct FlexArray {
|
|||
static std::unique_ptr<FlexArray> Create(index_type count)
|
||||
{
|
||||
void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))};
|
||||
return std::unique_ptr<FlexArray>{new(ptr) FlexArray{count}};
|
||||
return std::unique_ptr<FlexArray>{al::construct_at(static_cast<FlexArray*>(ptr), count)};
|
||||
}
|
||||
|
||||
FlexArray(index_type size) : mStore{size} { }
|
||||
|
|
|
|||
36
Engine/lib/openal-soft/common/alnumbers.h
Normal file
36
Engine/lib/openal-soft/common/alnumbers.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef COMMON_ALNUMBERS_H
|
||||
#define COMMON_ALNUMBERS_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace al {
|
||||
|
||||
namespace numbers {
|
||||
|
||||
namespace detail_ {
|
||||
template<typename T>
|
||||
using as_fp = std::enable_if_t<std::is_floating_point<T>::value, T>;
|
||||
} // detail_
|
||||
|
||||
template<typename T>
|
||||
static constexpr auto pi_v = detail_::as_fp<T>(3.141592653589793238462643383279502884L);
|
||||
|
||||
template<typename T>
|
||||
static constexpr auto inv_pi_v = detail_::as_fp<T>(0.318309886183790671537767526745028724L);
|
||||
|
||||
template<typename T>
|
||||
static constexpr auto sqrt2_v = detail_::as_fp<T>(1.414213562373095048801688724209698079L);
|
||||
|
||||
template<typename T>
|
||||
static constexpr auto sqrt3_v = detail_::as_fp<T>(1.732050807568877293527446341505872367L);
|
||||
|
||||
static constexpr auto pi = pi_v<double>;
|
||||
static constexpr auto inv_pi = inv_pi_v<double>;
|
||||
static constexpr auto sqrt2 = sqrt2_v<double>;
|
||||
static constexpr auto sqrt3 = sqrt3_v<double>;
|
||||
|
||||
} // namespace numbers
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* COMMON_ALNUMBERS_H */
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef AL_NUMERIC_H
|
||||
#define AL_NUMERIC_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#ifdef HAVE_INTRIN_H
|
||||
|
|
@ -67,7 +69,7 @@ constexpr inline size_t clampz(size_t val, size_t min, size_t max) noexcept
|
|||
{ return minz(max, maxz(min, val)); }
|
||||
|
||||
|
||||
constexpr inline float lerp(float val1, float val2, float mu) noexcept
|
||||
constexpr inline float lerpf(float val1, float val2, float mu) noexcept
|
||||
{ return val1 + (val2-val1)*mu; }
|
||||
constexpr inline float cubic(float val1, float val2, float val3, float val4, float mu) noexcept
|
||||
{
|
||||
|
|
@ -271,4 +273,27 @@ inline float fast_roundf(float f) noexcept
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr const T& clamp(const T& value, const T& min_value, const T& max_value) noexcept
|
||||
{
|
||||
return std::min(std::max(value, min_value), max_value);
|
||||
}
|
||||
|
||||
// Converts level (mB) to gain.
|
||||
inline float level_mb_to_gain(float x)
|
||||
{
|
||||
if(x <= -10'000.0f)
|
||||
return 0.0f;
|
||||
return std::pow(10.0f, x / 2'000.0f);
|
||||
}
|
||||
|
||||
// Converts gain to level (mB).
|
||||
inline float gain_to_level_mb(float x)
|
||||
{
|
||||
if (x <= 0.0f)
|
||||
return -10'000.0f;
|
||||
return maxf(std::log10(x) * 2'000.0f, -10'000.0f);
|
||||
}
|
||||
|
||||
#endif /* AL_NUMERIC_H */
|
||||
|
|
|
|||
|
|
@ -15,147 +15,339 @@ struct in_place_t { };
|
|||
constexpr nullopt_t nullopt{};
|
||||
constexpr in_place_t in_place{};
|
||||
|
||||
#define NOEXCEPT_AS(...) noexcept(noexcept(__VA_ARGS__))
|
||||
|
||||
namespace detail_ {
|
||||
/* Base storage struct for an optional. Defines a trivial destructor, for types
|
||||
* that can be trivially destructed.
|
||||
*/
|
||||
template<typename T, bool = std::is_trivially_destructible<T>::value>
|
||||
struct optstore_base {
|
||||
bool mHasValue{false};
|
||||
union {
|
||||
char mDummy{};
|
||||
T mValue;
|
||||
};
|
||||
|
||||
constexpr optstore_base() noexcept { }
|
||||
template<typename ...Args>
|
||||
constexpr explicit optstore_base(in_place_t, Args&& ...args)
|
||||
noexcept(std::is_nothrow_constructible<T, Args...>::value)
|
||||
: mHasValue{true}, mValue{std::forward<Args>(args)...}
|
||||
{ }
|
||||
~optstore_base() = default;
|
||||
};
|
||||
|
||||
/* Specialization needing a non-trivial destructor. */
|
||||
template<typename T>
|
||||
struct optstore_base<T, false> {
|
||||
bool mHasValue{false};
|
||||
union {
|
||||
char mDummy{};
|
||||
T mValue;
|
||||
};
|
||||
|
||||
constexpr optstore_base() noexcept { }
|
||||
template<typename ...Args>
|
||||
constexpr explicit optstore_base(in_place_t, Args&& ...args)
|
||||
noexcept(std::is_nothrow_constructible<T, Args...>::value)
|
||||
: mHasValue{true}, mValue{std::forward<Args>(args)...}
|
||||
{ }
|
||||
~optstore_base() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
|
||||
};
|
||||
|
||||
/* Next level of storage, which defines helpers to construct and destruct the
|
||||
* stored object.
|
||||
*/
|
||||
template<typename T>
|
||||
struct optstore_helper : public optstore_base<T> {
|
||||
using optstore_base<T>::optstore_base;
|
||||
|
||||
template<typename... Args>
|
||||
constexpr void construct(Args&& ...args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
|
||||
{
|
||||
al::construct_at(std::addressof(this->mValue), std::forward<Args>(args)...);
|
||||
this->mHasValue = true;
|
||||
}
|
||||
|
||||
constexpr void reset() noexcept
|
||||
{
|
||||
if(this->mHasValue)
|
||||
al::destroy_at(std::addressof(this->mValue));
|
||||
this->mHasValue = false;
|
||||
}
|
||||
|
||||
constexpr void assign(const optstore_helper &rhs)
|
||||
noexcept(std::is_nothrow_copy_constructible<T>::value
|
||||
&& std::is_nothrow_copy_assignable<T>::value)
|
||||
{
|
||||
if(!rhs.mHasValue)
|
||||
this->reset();
|
||||
else if(this->mHasValue)
|
||||
this->mValue = rhs.mValue;
|
||||
else
|
||||
this->construct(rhs.mValue);
|
||||
}
|
||||
|
||||
constexpr void assign(optstore_helper&& rhs)
|
||||
noexcept(std::is_nothrow_move_constructible<T>::value
|
||||
&& std::is_nothrow_move_assignable<T>::value)
|
||||
{
|
||||
if(!rhs.mHasValue)
|
||||
this->reset();
|
||||
else if(this->mHasValue)
|
||||
this->mValue = std::move(rhs.mValue);
|
||||
else
|
||||
this->construct(std::move(rhs.mValue));
|
||||
}
|
||||
};
|
||||
|
||||
/* Define copy and move constructors and assignment operators, which may or may
|
||||
* not be trivial.
|
||||
*/
|
||||
template<typename T, bool trivial_copy = std::is_trivially_copy_constructible<T>::value,
|
||||
bool trivial_move = std::is_trivially_move_constructible<T>::value,
|
||||
/* Trivial assignment is dependent on trivial construction+destruction. */
|
||||
bool = trivial_copy && std::is_trivially_copy_assignable<T>::value
|
||||
&& std::is_trivially_destructible<T>::value,
|
||||
bool = trivial_move && std::is_trivially_move_assignable<T>::value
|
||||
&& std::is_trivially_destructible<T>::value>
|
||||
struct optional_storage;
|
||||
|
||||
template<typename T>
|
||||
struct optional_storage<T, true> {
|
||||
bool mHasValue{false};
|
||||
union {
|
||||
char mDummy;
|
||||
T mValue;
|
||||
};
|
||||
/* Some versions of GCC have issues with 'this' in the following noexcept(...)
|
||||
* statements, so this macro is a workaround.
|
||||
*/
|
||||
#define _this std::declval<optional_storage*>()
|
||||
|
||||
optional_storage() { }
|
||||
template<typename ...Args>
|
||||
explicit optional_storage(in_place_t, Args&& ...args)
|
||||
: mHasValue{true}, mValue{std::forward<Args>(args)...}
|
||||
{ }
|
||||
~optional_storage() = default;
|
||||
/* Completely trivial. */
|
||||
template<typename T>
|
||||
struct optional_storage<T, true, true, true, true> : public optstore_helper<T> {
|
||||
using optstore_helper<T>::optstore_helper;
|
||||
constexpr optional_storage() noexcept = default;
|
||||
constexpr optional_storage(const optional_storage&) = default;
|
||||
constexpr optional_storage(optional_storage&&) = default;
|
||||
constexpr optional_storage& operator=(const optional_storage&) = default;
|
||||
constexpr optional_storage& operator=(optional_storage&&) = default;
|
||||
};
|
||||
|
||||
/* Non-trivial move assignment. */
|
||||
template<typename T>
|
||||
struct optional_storage<T, false> {
|
||||
bool mHasValue{false};
|
||||
union {
|
||||
char mDummy;
|
||||
T mValue;
|
||||
};
|
||||
|
||||
optional_storage() { }
|
||||
template<typename ...Args>
|
||||
explicit optional_storage(in_place_t, Args&& ...args)
|
||||
: mHasValue{true}, mValue{std::forward<Args>(args)...}
|
||||
{ }
|
||||
~optional_storage() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
|
||||
struct optional_storage<T, true, true, true, false> : public optstore_helper<T> {
|
||||
using optstore_helper<T>::optstore_helper;
|
||||
constexpr optional_storage() noexcept = default;
|
||||
constexpr optional_storage(const optional_storage&) = default;
|
||||
constexpr optional_storage(optional_storage&&) = default;
|
||||
constexpr optional_storage& operator=(const optional_storage&) = default;
|
||||
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
|
||||
{ this->assign(std::move(rhs)); return *this; }
|
||||
};
|
||||
|
||||
/* Non-trivial move construction. */
|
||||
template<typename T>
|
||||
struct optional_storage<T, true, false, true, false> : public optstore_helper<T> {
|
||||
using optstore_helper<T>::optstore_helper;
|
||||
constexpr optional_storage() noexcept = default;
|
||||
constexpr optional_storage(const optional_storage&) = default;
|
||||
constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
|
||||
{ if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
|
||||
constexpr optional_storage& operator=(const optional_storage&) = default;
|
||||
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
|
||||
{ this->assign(std::move(rhs)); return *this; }
|
||||
};
|
||||
|
||||
/* Non-trivial copy assignment. */
|
||||
template<typename T>
|
||||
struct optional_storage<T, true, true, false, true> : public optstore_helper<T> {
|
||||
using optstore_helper<T>::optstore_helper;
|
||||
constexpr optional_storage() noexcept = default;
|
||||
constexpr optional_storage(const optional_storage&) = default;
|
||||
constexpr optional_storage(optional_storage&&) = default;
|
||||
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
|
||||
{ this->assign(rhs); return *this; }
|
||||
constexpr optional_storage& operator=(optional_storage&&) = default;
|
||||
};
|
||||
|
||||
/* Non-trivial copy construction. */
|
||||
template<typename T>
|
||||
struct optional_storage<T, false, true, false, true> : public optstore_helper<T> {
|
||||
using optstore_helper<T>::optstore_helper;
|
||||
constexpr optional_storage() noexcept = default;
|
||||
constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
|
||||
{ if(rhs.mHasValue) this->construct(rhs.mValue); }
|
||||
constexpr optional_storage(optional_storage&&) = default;
|
||||
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
|
||||
{ this->assign(rhs); return *this; }
|
||||
constexpr optional_storage& operator=(optional_storage&&) = default;
|
||||
};
|
||||
|
||||
/* Non-trivial assignment. */
|
||||
template<typename T>
|
||||
struct optional_storage<T, true, true, false, false> : public optstore_helper<T> {
|
||||
using optstore_helper<T>::optstore_helper;
|
||||
constexpr optional_storage() noexcept = default;
|
||||
constexpr optional_storage(const optional_storage&) = default;
|
||||
constexpr optional_storage(optional_storage&&) = default;
|
||||
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
|
||||
{ this->assign(rhs); return *this; }
|
||||
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
|
||||
{ this->assign(std::move(rhs)); return *this; }
|
||||
};
|
||||
|
||||
/* Non-trivial assignment, non-trivial move construction. */
|
||||
template<typename T>
|
||||
struct optional_storage<T, true, false, false, false> : public optstore_helper<T> {
|
||||
using optstore_helper<T>::optstore_helper;
|
||||
constexpr optional_storage() noexcept = default;
|
||||
constexpr optional_storage(const optional_storage&) = default;
|
||||
constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
|
||||
{ if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
|
||||
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
|
||||
{ this->assign(rhs); return *this; }
|
||||
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
|
||||
{ this->assign(std::move(rhs)); return *this; }
|
||||
};
|
||||
|
||||
/* Non-trivial assignment, non-trivial copy construction. */
|
||||
template<typename T>
|
||||
struct optional_storage<T, false, true, false, false> : public optstore_helper<T> {
|
||||
using optstore_helper<T>::optstore_helper;
|
||||
constexpr optional_storage() noexcept = default;
|
||||
constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
|
||||
{ if(rhs.mHasValue) this->construct(rhs.mValue); }
|
||||
constexpr optional_storage(optional_storage&&) = default;
|
||||
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
|
||||
{ this->assign(rhs); return *this; }
|
||||
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
|
||||
{ this->assign(std::move(rhs)); return *this; }
|
||||
};
|
||||
|
||||
/* Completely non-trivial. */
|
||||
template<typename T>
|
||||
struct optional_storage<T, false, false, false, false> : public optstore_helper<T> {
|
||||
using optstore_helper<T>::optstore_helper;
|
||||
constexpr optional_storage() noexcept = default;
|
||||
constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
|
||||
{ if(rhs.mHasValue) this->construct(rhs.mValue); }
|
||||
constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
|
||||
{ if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
|
||||
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
|
||||
{ this->assign(rhs); return *this; }
|
||||
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
|
||||
{ this->assign(std::move(rhs)); return *this; }
|
||||
};
|
||||
|
||||
#undef _this
|
||||
|
||||
} // namespace detail_
|
||||
|
||||
#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
|
||||
|
||||
template<typename T>
|
||||
class optional {
|
||||
using storage_t = optional_storage<T>;
|
||||
using storage_t = detail_::optional_storage<T>;
|
||||
|
||||
storage_t mStore;
|
||||
|
||||
template<typename... Args>
|
||||
void doConstruct(Args&& ...args)
|
||||
{
|
||||
::new(std::addressof(mStore.mValue)) T{std::forward<Args>(args)...};
|
||||
mStore.mHasValue = true;
|
||||
}
|
||||
storage_t mStore{};
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
optional() = default;
|
||||
optional(nullopt_t) noexcept { }
|
||||
optional(const optional &rhs) { if(rhs) doConstruct(*rhs); }
|
||||
optional(optional&& rhs) { if(rhs) doConstruct(std::move(*rhs)); }
|
||||
constexpr optional() = default;
|
||||
constexpr optional(const optional&) = default;
|
||||
constexpr optional(optional&&) = default;
|
||||
constexpr optional(nullopt_t) noexcept { }
|
||||
template<typename ...Args>
|
||||
explicit optional(in_place_t, Args&& ...args)
|
||||
constexpr explicit optional(in_place_t, Args&& ...args)
|
||||
NOEXCEPT_AS(storage_t{al::in_place, std::forward<Args>(args)...})
|
||||
: mStore{al::in_place, std::forward<Args>(args)...}
|
||||
{ }
|
||||
template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
|
||||
&& !std::is_same<std::decay_t<U>, al::in_place_t>::value
|
||||
&& !std::is_same<std::decay_t<U>, optional<T>>::value
|
||||
&& std::is_convertible<U&&, T>::value)>
|
||||
constexpr optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
|
||||
: mStore{al::in_place, std::forward<U>(rhs)}
|
||||
{ }
|
||||
template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
|
||||
&& !std::is_same<std::decay_t<U>, al::in_place_t>::value
|
||||
&& !std::is_same<std::decay_t<U>, optional<T>>::value
|
||||
&& !std::is_convertible<U&&, T>::value)>
|
||||
constexpr explicit optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
|
||||
: mStore{al::in_place, std::forward<U>(rhs)}
|
||||
{ }
|
||||
~optional() = default;
|
||||
|
||||
optional& operator=(nullopt_t) noexcept { reset(); return *this; }
|
||||
std::enable_if_t<std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value,
|
||||
optional&> operator=(const optional &rhs)
|
||||
{
|
||||
if(!rhs)
|
||||
reset();
|
||||
else if(*this)
|
||||
mStore.mValue = *rhs;
|
||||
else
|
||||
doConstruct(*rhs);
|
||||
return *this;
|
||||
}
|
||||
std::enable_if_t<std::is_move_constructible<T>::value && std::is_move_assignable<T>::value,
|
||||
optional&> operator=(optional&& rhs)
|
||||
{
|
||||
if(!rhs)
|
||||
reset();
|
||||
else if(*this)
|
||||
mStore.mValue = std::move(*rhs);
|
||||
else
|
||||
doConstruct(std::move(*rhs));
|
||||
return *this;
|
||||
}
|
||||
constexpr optional& operator=(const optional&) = default;
|
||||
constexpr optional& operator=(optional&&) = default;
|
||||
constexpr optional& operator=(nullopt_t) noexcept { mStore.reset(); return *this; }
|
||||
template<typename U=T>
|
||||
std::enable_if_t<std::is_constructible<T, U>::value
|
||||
constexpr std::enable_if_t<std::is_constructible<T, U>::value
|
||||
&& std::is_assignable<T&, U>::value
|
||||
&& !std::is_same<std::decay_t<U>, optional<T>>::value
|
||||
&& (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value),
|
||||
optional&> operator=(U&& rhs)
|
||||
{
|
||||
if(*this)
|
||||
if(mStore.mHasValue)
|
||||
mStore.mValue = std::forward<U>(rhs);
|
||||
else
|
||||
doConstruct(std::forward<U>(rhs));
|
||||
mStore.construct(std::forward<U>(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T* operator->() const { return std::addressof(mStore.mValue); }
|
||||
T* operator->() { return std::addressof(mStore.mValue); }
|
||||
const T& operator*() const& { return this->mValue; }
|
||||
T& operator*() & { return mStore.mValue; }
|
||||
const T&& operator*() const&& { return std::move(mStore.mValue); }
|
||||
T&& operator*() && { return std::move(mStore.mValue); }
|
||||
constexpr const T* operator->() const { return std::addressof(mStore.mValue); }
|
||||
constexpr T* operator->() { return std::addressof(mStore.mValue); }
|
||||
constexpr const T& operator*() const& { return mStore.mValue; }
|
||||
constexpr T& operator*() & { return mStore.mValue; }
|
||||
constexpr const T&& operator*() const&& { return std::move(mStore.mValue); }
|
||||
constexpr T&& operator*() && { return std::move(mStore.mValue); }
|
||||
|
||||
operator bool() const noexcept { return mStore.mHasValue; }
|
||||
bool has_value() const noexcept { return mStore.mHasValue; }
|
||||
constexpr explicit operator bool() const noexcept { return mStore.mHasValue; }
|
||||
constexpr bool has_value() const noexcept { return mStore.mHasValue; }
|
||||
|
||||
T& value() & { return mStore.mValue; }
|
||||
const T& value() const& { return mStore.mValue; }
|
||||
T&& value() && { return std::move(mStore.mValue); }
|
||||
const T&& value() const&& { return std::move(mStore.mValue); }
|
||||
constexpr T& value() & { return mStore.mValue; }
|
||||
constexpr const T& value() const& { return mStore.mValue; }
|
||||
constexpr T&& value() && { return std::move(mStore.mValue); }
|
||||
constexpr const T&& value() const&& { return std::move(mStore.mValue); }
|
||||
|
||||
template<typename U>
|
||||
T value_or(U&& defval) const&
|
||||
constexpr T value_or(U&& defval) const&
|
||||
{ return bool{*this} ? **this : static_cast<T>(std::forward<U>(defval)); }
|
||||
template<typename U>
|
||||
T value_or(U&& defval) &&
|
||||
constexpr T value_or(U&& defval) &&
|
||||
{ return bool{*this} ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
|
||||
|
||||
void reset() noexcept
|
||||
template<typename ...Args>
|
||||
constexpr T& emplace(Args&& ...args)
|
||||
{
|
||||
if(mStore.mHasValue)
|
||||
al::destroy_at(std::addressof(mStore.mValue));
|
||||
mStore.mHasValue = false;
|
||||
mStore.reset();
|
||||
mStore.construct(std::forward<Args>(args)...);
|
||||
return mStore.mValue;
|
||||
}
|
||||
template<typename U, typename ...Args>
|
||||
constexpr std::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
|
||||
T&> emplace(std::initializer_list<U> il, Args&& ...args)
|
||||
{
|
||||
mStore.reset();
|
||||
mStore.construct(il, std::forward<Args>(args)...);
|
||||
return mStore.mValue;
|
||||
}
|
||||
|
||||
constexpr void reset() noexcept { mStore.reset(); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline optional<std::decay_t<T>> make_optional(T&& arg)
|
||||
constexpr optional<std::decay_t<T>> make_optional(T&& arg)
|
||||
{ return optional<std::decay_t<T>>{in_place, std::forward<T>(arg)}; }
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline optional<T> make_optional(Args&& ...args)
|
||||
constexpr optional<T> make_optional(Args&& ...args)
|
||||
{ return optional<T>{in_place, std::forward<Args>(args)...}; }
|
||||
|
||||
template<typename T, typename U, typename... Args>
|
||||
inline optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
|
||||
constexpr optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
|
||||
{ return optional<T>{in_place, il, std::forward<Args>(args)...}; }
|
||||
|
||||
#undef REQUIRES
|
||||
#undef NOEXCEPT_AS
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_OPTIONAL_H */
|
||||
|
|
|
|||
|
|
@ -9,22 +9,14 @@
|
|||
|
||||
namespace al {
|
||||
|
||||
template<typename T>
|
||||
constexpr auto size(T &cont) noexcept(noexcept(cont.size())) -> decltype(cont.size())
|
||||
{ return cont.size(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr auto size(const T &cont) noexcept(noexcept(cont.size())) -> decltype(cont.size())
|
||||
{ return cont.size(); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
constexpr size_t size(T (&)[N]) noexcept
|
||||
constexpr size_t size(const T (&)[N]) noexcept
|
||||
{ return N; }
|
||||
|
||||
template<typename T>
|
||||
constexpr size_t size(std::initializer_list<T> list) noexcept
|
||||
{ return list.size(); }
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr auto data(T &cont) noexcept(noexcept(cont.data())) -> decltype(cont.data())
|
||||
|
|
|
|||
|
|
@ -2,23 +2,10 @@
|
|||
#define AL_STRING_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
namespace al {
|
||||
|
||||
template<typename T, typename Tr=std::char_traits<T>>
|
||||
using basic_string = std::basic_string<T, Tr, al::allocator<T>>;
|
||||
|
||||
using string = basic_string<char>;
|
||||
using wstring = basic_string<wchar_t>;
|
||||
using u16string = basic_string<char16_t>;
|
||||
using u32string = basic_string<char32_t>;
|
||||
|
||||
|
||||
/* These would be better served by using a string_view-like span/view with
|
||||
* case-insensitive char traits.
|
||||
*/
|
||||
|
|
|
|||
68
Engine/lib/openal-soft/common/comptr.h
Normal file
68
Engine/lib/openal-soft/common/comptr.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#ifndef COMMON_COMPTR_H
|
||||
#define COMMON_COMPTR_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
template<typename T>
|
||||
class ComPtr {
|
||||
T *mPtr{nullptr};
|
||||
|
||||
public:
|
||||
ComPtr() noexcept = default;
|
||||
ComPtr(const ComPtr &rhs) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); }
|
||||
ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; }
|
||||
ComPtr(std::nullptr_t) noexcept { }
|
||||
explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { }
|
||||
~ComPtr() { if(mPtr) mPtr->Release(); }
|
||||
|
||||
ComPtr& operator=(const ComPtr &rhs)
|
||||
{
|
||||
if(!rhs.mPtr)
|
||||
{
|
||||
if(mPtr)
|
||||
mPtr->Release();
|
||||
mPtr = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs.mPtr->AddRef();
|
||||
try {
|
||||
if(mPtr)
|
||||
mPtr->Release();
|
||||
mPtr = rhs.mPtr;
|
||||
}
|
||||
catch(...) {
|
||||
rhs.mPtr->Release();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
ComPtr& operator=(ComPtr&& rhs)
|
||||
{
|
||||
if(likely(&rhs != this))
|
||||
{
|
||||
if(mPtr) mPtr->Release();
|
||||
mPtr = std::exchange(rhs.mPtr, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept { return mPtr != nullptr; }
|
||||
|
||||
T& operator*() const noexcept { return *mPtr; }
|
||||
T* operator->() const noexcept { return mPtr; }
|
||||
T* get() const noexcept { return mPtr; }
|
||||
T** getPtr() noexcept { return &mPtr; }
|
||||
|
||||
T* release() noexcept { return std::exchange(mPtr, nullptr); }
|
||||
|
||||
void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
||||
void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef INTRUSIVE_PTR_H
|
||||
#define INTRUSIVE_PTR_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "atomic.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
|
@ -60,6 +62,8 @@ public:
|
|||
|
||||
intrusive_ptr& operator=(const intrusive_ptr &rhs) noexcept
|
||||
{
|
||||
static_assert(noexcept(std::declval<T*>()->release()), "release must be noexcept");
|
||||
|
||||
if(rhs.mPtr) rhs.mPtr->add_ref();
|
||||
if(mPtr) mPtr->release();
|
||||
mPtr = rhs.mPtr;
|
||||
|
|
@ -67,14 +71,15 @@ public:
|
|||
}
|
||||
intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept
|
||||
{
|
||||
if(mPtr)
|
||||
mPtr->release();
|
||||
mPtr = rhs.mPtr;
|
||||
rhs.mPtr = nullptr;
|
||||
if(likely(&rhs != this))
|
||||
{
|
||||
if(mPtr) mPtr->release();
|
||||
mPtr = std::exchange(rhs.mPtr, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const noexcept { return mPtr != nullptr; }
|
||||
explicit operator bool() const noexcept { return mPtr != nullptr; }
|
||||
|
||||
T& operator*() const noexcept { return *mPtr; }
|
||||
T* operator->() const noexcept { return mPtr; }
|
||||
|
|
@ -87,12 +92,7 @@ public:
|
|||
mPtr = ptr;
|
||||
}
|
||||
|
||||
T* release() noexcept
|
||||
{
|
||||
T *ret{mPtr};
|
||||
mPtr = nullptr;
|
||||
return ret;
|
||||
}
|
||||
T* release() noexcept { return std::exchange(mPtr, nullptr); }
|
||||
|
||||
void swap(intrusive_ptr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
||||
void swap(intrusive_ptr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef AL_MATH_DEFS_H
|
||||
#define AL_MATH_DEFS_H
|
||||
|
||||
constexpr float Deg2Rad(float x) noexcept { return x * 1.74532925199432955e-02f/*pi/180*/; }
|
||||
constexpr float Rad2Deg(float x) noexcept { return x * 5.72957795130823229e+01f/*180/pi*/; }
|
||||
|
||||
namespace al {
|
||||
|
||||
template<typename Real>
|
||||
struct MathDefs { };
|
||||
|
||||
template<>
|
||||
struct MathDefs<float> {
|
||||
static constexpr float Pi() noexcept { return 3.14159265358979323846e+00f; }
|
||||
static constexpr float Tau() noexcept { return 6.28318530717958647692e+00f; }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct MathDefs<double> {
|
||||
static constexpr double Pi() noexcept { return 3.14159265358979323846e+00; }
|
||||
static constexpr double Tau() noexcept { return 6.28318530717958647692e+00; }
|
||||
};
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_MATH_DEFS_H */
|
||||
|
|
@ -1,26 +1,50 @@
|
|||
#ifndef OPTHELPERS_H
|
||||
#define OPTHELPERS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
|
||||
#ifdef __has_builtin
|
||||
#define HAS_BUILTIN __has_builtin
|
||||
#else
|
||||
#define HAS_BUILTIN(x) (0)
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define force_inline [[gnu::always_inline]]
|
||||
#elif defined(_MSC_VER)
|
||||
#define force_inline __forceinline
|
||||
#else
|
||||
#define force_inline inline
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || HAS_BUILTIN(__builtin_expect)
|
||||
/* LIKELY optimizes the case where the condition is true. The condition is not
|
||||
* required to be true, but it can result in more optimal code for the true
|
||||
* path at the expense of a less optimal false path.
|
||||
/* likely() optimizes for the case where the condition is true. The condition
|
||||
* is not required to be true, but it can result in more optimal code for the
|
||||
* true path at the expense of a less optimal false path.
|
||||
*/
|
||||
#define LIKELY(x) (__builtin_expect(!!(x), !false))
|
||||
/* The opposite of LIKELY, optimizing the case where the condition is false. */
|
||||
#define UNLIKELY(x) (__builtin_expect(!!(x), false))
|
||||
template<typename T>
|
||||
force_inline constexpr bool likely(T&& expr) noexcept
|
||||
{ return __builtin_expect(static_cast<bool>(std::forward<T>(expr)), true); }
|
||||
/* The opposite of likely(), optimizing for the case where the condition is
|
||||
* false.
|
||||
*/
|
||||
template<typename T>
|
||||
force_inline constexpr bool unlikely(T&& expr) noexcept
|
||||
{ return __builtin_expect(static_cast<bool>(std::forward<T>(expr)), false); }
|
||||
|
||||
#else
|
||||
|
||||
#define LIKELY(x) (!!(x))
|
||||
#define UNLIKELY(x) (!!(x))
|
||||
template<typename T>
|
||||
force_inline constexpr bool likely(T&& expr) noexcept
|
||||
{ return static_cast<bool>(std::forward<T>(expr)); }
|
||||
template<typename T>
|
||||
force_inline constexpr bool unlikely(T&& expr) noexcept
|
||||
{ return static_cast<bool>(std::forward<T>(expr)); }
|
||||
#endif
|
||||
#define LIKELY(x) (likely(x))
|
||||
#define UNLIKELY(x) (unlikely(x))
|
||||
|
||||
#if HAS_BUILTIN(__builtin_assume)
|
||||
/* Unlike LIKELY, ASSUME requires the condition to be true or else it invokes
|
||||
|
|
@ -31,15 +55,33 @@
|
|||
#elif defined(_MSC_VER)
|
||||
#define ASSUME __assume
|
||||
#elif defined(__GNUC__)
|
||||
#define ASSUME(x) do { if(!(x)) __builtin_unreachable(); } while(0)
|
||||
#define ASSUME(x) do { if(x) break; __builtin_unreachable(); } while(0)
|
||||
#else
|
||||
#define ASSUME(x) ((void)0)
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L || defined(__cpp_if_constexpr)
|
||||
#define if_constexpr if constexpr
|
||||
namespace al {
|
||||
|
||||
template<std::size_t alignment, typename T>
|
||||
force_inline constexpr auto assume_aligned(T *ptr) noexcept
|
||||
{
|
||||
#ifdef __cpp_lib_assume_aligned
|
||||
return std::assume_aligned<alignment,T>(ptr);
|
||||
#elif defined(__clang__) || (defined(__GNUC__) && !defined(__ICC))
|
||||
return static_cast<T*>(__builtin_assume_aligned(ptr, alignment));
|
||||
#elif defined(_MSC_VER)
|
||||
constexpr std::size_t alignment_mask{(1<<alignment) - 1};
|
||||
if((reinterpret_cast<std::uintptr_t>(ptr)&alignment_mask) == 0)
|
||||
return ptr;
|
||||
__assume(0);
|
||||
#elif defined(__ICC)
|
||||
__assume_aligned(ptr, alignment);
|
||||
return ptr;
|
||||
#else
|
||||
#define if_constexpr if
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* OPTHELPERS_H */
|
||||
|
|
|
|||
314
Engine/lib/openal-soft/common/phase_shifter.h
Normal file
314
Engine/lib/openal-soft/common/phase_shifter.h
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
#ifndef PHASE_SHIFTER_H
|
||||
#define PHASE_SHIFTER_H
|
||||
|
||||
#ifdef HAVE_SSE_INTRINSICS
|
||||
#include <xmmintrin.h>
|
||||
#elif defined(HAVE_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "alcomplex.h"
|
||||
#include "alspan.h"
|
||||
|
||||
|
||||
/* Implements a wide-band +90 degree phase-shift. Note that this should be
|
||||
* given one sample less of a delay (FilterSize/2 - 1) compared to the direct
|
||||
* signal delay (FilterSize/2) to properly align.
|
||||
*/
|
||||
template<size_t FilterSize>
|
||||
struct PhaseShifterT {
|
||||
static_assert(FilterSize >= 16, "FilterSize needs to be at least 16");
|
||||
static_assert((FilterSize&(FilterSize-1)) == 0, "FilterSize needs to be power-of-two");
|
||||
|
||||
alignas(16) std::array<float,FilterSize/2> mCoeffs{};
|
||||
|
||||
/* Some notes on this filter construction.
|
||||
*
|
||||
* A wide-band phase-shift filter needs a delay to maintain linearity. A
|
||||
* dirac impulse in the center of a time-domain buffer represents a filter
|
||||
* passing all frequencies through as-is with a pure delay. Converting that
|
||||
* to the frequency domain, adjusting the phase of each frequency bin by
|
||||
* +90 degrees, then converting back to the time domain, results in a FIR
|
||||
* filter that applies a +90 degree wide-band phase-shift.
|
||||
*
|
||||
* A particularly notable aspect of the time-domain filter response is that
|
||||
* every other coefficient is 0. This allows doubling the effective size of
|
||||
* the filter, by storing only the non-0 coefficients and double-stepping
|
||||
* over the input to apply it.
|
||||
*
|
||||
* Additionally, the resulting filter is independent of the sample rate.
|
||||
* The same filter can be applied regardless of the device's sample rate
|
||||
* and achieve the same effect.
|
||||
*/
|
||||
PhaseShifterT()
|
||||
{
|
||||
using complex_d = std::complex<double>;
|
||||
constexpr size_t fft_size{FilterSize};
|
||||
constexpr size_t half_size{fft_size / 2};
|
||||
|
||||
auto fftBuffer = std::make_unique<complex_d[]>(fft_size);
|
||||
std::fill_n(fftBuffer.get(), fft_size, complex_d{});
|
||||
fftBuffer[half_size] = 1.0;
|
||||
|
||||
forward_fft({fftBuffer.get(), fft_size});
|
||||
for(size_t i{0};i < half_size+1;++i)
|
||||
fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()};
|
||||
for(size_t i{half_size+1};i < fft_size;++i)
|
||||
fftBuffer[i] = std::conj(fftBuffer[fft_size - i]);
|
||||
inverse_fft({fftBuffer.get(), fft_size});
|
||||
|
||||
auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1);
|
||||
for(float &coeff : mCoeffs)
|
||||
{
|
||||
coeff = static_cast<float>(fftiter->real() / double{fft_size});
|
||||
fftiter -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void process(al::span<float> dst, const float *RESTRICT src) const;
|
||||
void processAccum(al::span<float> dst, const float *RESTRICT src) const;
|
||||
|
||||
private:
|
||||
#if defined(HAVE_NEON)
|
||||
/* There doesn't seem to be NEON intrinsics to do this kind of stipple
|
||||
* shuffling, so there's two custom methods for it.
|
||||
*/
|
||||
static auto shuffle_2020(float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))};
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3);
|
||||
return ret;
|
||||
}
|
||||
static auto shuffle_3131(float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))};
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3);
|
||||
return ret;
|
||||
}
|
||||
static auto unpacklo(float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))};
|
||||
return vcombine_f32(result.val[0], result.val[1]);
|
||||
}
|
||||
static auto unpackhi(float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))};
|
||||
return vcombine_f32(result.val[0], result.val[1]);
|
||||
}
|
||||
static auto load4(float32_t a, float32_t b, float32_t c, float32_t d)
|
||||
{
|
||||
float32x4_t ret{vmovq_n_f32(a)};
|
||||
ret = vsetq_lane_f32(b, ret, 1);
|
||||
ret = vsetq_lane_f32(c, ret, 2);
|
||||
ret = vsetq_lane_f32(d, ret, 3);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template<size_t S>
|
||||
inline void PhaseShifterT<S>::process(al::span<float> dst, const float *RESTRICT src) const
|
||||
{
|
||||
#ifdef HAVE_SSE_INTRINSICS
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
auto *out = reinterpret_cast<__m64*>(dst.data());
|
||||
do {
|
||||
__m128 r04{_mm_setzero_ps()};
|
||||
__m128 r14{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
const __m128 s0{_mm_loadu_ps(&src[j*2])};
|
||||
const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])};
|
||||
|
||||
__m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))};
|
||||
r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs));
|
||||
|
||||
s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1));
|
||||
r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
src += 2;
|
||||
|
||||
__m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))};
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
_mm_storel_pi(out, r4);
|
||||
++out;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
__m128 r4{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
dst.back() = _mm_cvtss_f32(r4);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_NEON)
|
||||
|
||||
size_t pos{0};
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
do {
|
||||
float32x4_t r04{vdupq_n_f32(0.0f)};
|
||||
float32x4_t r14{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s0{vld1q_f32(&src[j*2])};
|
||||
const float32x4_t s1{vld1q_f32(&src[j*2 + 4])};
|
||||
|
||||
r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs);
|
||||
r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs);
|
||||
}
|
||||
src += 2;
|
||||
|
||||
float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))};
|
||||
float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))};
|
||||
|
||||
vst1_f32(&dst[pos], r2);
|
||||
pos += 2;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
float32x4_t r4{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = vmlaq_f32(r4, s, coeffs);
|
||||
}
|
||||
r4 = vaddq_f32(r4, vrev64q_f32(r4));
|
||||
dst[pos] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for(float &output : dst)
|
||||
{
|
||||
float ret{0.0f};
|
||||
for(size_t j{0};j < mCoeffs.size();++j)
|
||||
ret += src[j*2] * mCoeffs[j];
|
||||
|
||||
output = ret;
|
||||
++src;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<size_t S>
|
||||
inline void PhaseShifterT<S>::processAccum(al::span<float> dst, const float *RESTRICT src) const
|
||||
{
|
||||
#ifdef HAVE_SSE_INTRINSICS
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
auto *out = reinterpret_cast<__m64*>(dst.data());
|
||||
do {
|
||||
__m128 r04{_mm_setzero_ps()};
|
||||
__m128 r14{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
const __m128 s0{_mm_loadu_ps(&src[j*2])};
|
||||
const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])};
|
||||
|
||||
__m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))};
|
||||
r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs));
|
||||
|
||||
s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1));
|
||||
r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
src += 2;
|
||||
|
||||
__m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))};
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
_mm_storel_pi(out, _mm_add_ps(_mm_loadl_pi(_mm_undefined_ps(), out), r4));
|
||||
++out;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
__m128 r4{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
dst.back() += _mm_cvtss_f32(r4);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_NEON)
|
||||
|
||||
size_t pos{0};
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
do {
|
||||
float32x4_t r04{vdupq_n_f32(0.0f)};
|
||||
float32x4_t r14{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s0{vld1q_f32(&src[j*2])};
|
||||
const float32x4_t s1{vld1q_f32(&src[j*2 + 4])};
|
||||
|
||||
r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs);
|
||||
r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs);
|
||||
}
|
||||
src += 2;
|
||||
|
||||
float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))};
|
||||
float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))};
|
||||
|
||||
vst1_f32(&dst[pos], vadd_f32(vld1_f32(&dst[pos]), r2));
|
||||
pos += 2;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
float32x4_t r4{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = vmlaq_f32(r4, s, coeffs);
|
||||
}
|
||||
r4 = vaddq_f32(r4, vrev64q_f32(r4));
|
||||
dst[pos] += vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for(float &output : dst)
|
||||
{
|
||||
float ret{0.0f};
|
||||
for(size_t j{0};j < mCoeffs.size();++j)
|
||||
ret += src[j*2] * mCoeffs[j];
|
||||
|
||||
output += ret;
|
||||
++src;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* PHASE_SHIFTER_H */
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "math_defs.h"
|
||||
#include "alnumbers.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
|
|
@ -21,9 +21,9 @@ using uint = unsigned int;
|
|||
*/
|
||||
double Sinc(const double x)
|
||||
{
|
||||
if UNLIKELY(std::abs(x) < Epsilon)
|
||||
if(unlikely(std::abs(x) < Epsilon))
|
||||
return 1.0;
|
||||
return std::sin(al::MathDefs<double>::Pi()*x) / (al::MathDefs<double>::Pi()*x);
|
||||
return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
|
||||
}
|
||||
|
||||
/* The zero-order modified Bessel function of the first kind, used for the
|
||||
|
|
@ -95,7 +95,7 @@ constexpr uint Gcd(uint x, uint y)
|
|||
*/
|
||||
constexpr uint CalcKaiserOrder(const double rejection, const double transition)
|
||||
{
|
||||
const double w_t{2.0 * al::MathDefs<double>::Pi() * transition};
|
||||
const double w_t{2.0 * al::numbers::pi * transition};
|
||||
if LIKELY(rejection > 21.0)
|
||||
return static_cast<uint>(std::ceil((rejection - 7.95) / (2.285 * w_t)));
|
||||
return static_cast<uint>(std::ceil(5.79 / w_t));
|
||||
|
|
|
|||
|
|
@ -160,9 +160,9 @@ size_t RingBuffer::write(const void *src, size_t cnt) noexcept
|
|||
}
|
||||
|
||||
|
||||
ll_ringbuffer_data_pair RingBuffer::getReadVector() const noexcept
|
||||
auto RingBuffer::getReadVector() const noexcept -> DataPair
|
||||
{
|
||||
ll_ringbuffer_data_pair ret;
|
||||
DataPair ret;
|
||||
|
||||
size_t w{mWritePtr.load(std::memory_order_acquire)};
|
||||
size_t r{mReadPtr.load(std::memory_order_acquire)};
|
||||
|
|
@ -192,9 +192,9 @@ ll_ringbuffer_data_pair RingBuffer::getReadVector() const noexcept
|
|||
return ret;
|
||||
}
|
||||
|
||||
ll_ringbuffer_data_pair RingBuffer::getWriteVector() const noexcept
|
||||
auto RingBuffer::getWriteVector() const noexcept -> DataPair
|
||||
{
|
||||
ll_ringbuffer_data_pair ret;
|
||||
DataPair ret;
|
||||
|
||||
size_t w{mWritePtr.load(std::memory_order_acquire)};
|
||||
size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask};
|
||||
|
|
|
|||
|
|
@ -16,13 +16,6 @@
|
|||
* single-consumer/single-provider operation.
|
||||
*/
|
||||
|
||||
struct ll_ringbuffer_data {
|
||||
al::byte *buf;
|
||||
size_t len;
|
||||
};
|
||||
using ll_ringbuffer_data_pair = std::pair<ll_ringbuffer_data,ll_ringbuffer_data>;
|
||||
|
||||
|
||||
struct RingBuffer {
|
||||
private:
|
||||
std::atomic<size_t> mWritePtr{0u};
|
||||
|
|
@ -34,6 +27,13 @@ private:
|
|||
al::FlexArray<al::byte, 16> mBuffer;
|
||||
|
||||
public:
|
||||
struct Data {
|
||||
al::byte *buf;
|
||||
size_t len;
|
||||
};
|
||||
using DataPair = std::pair<Data,Data>;
|
||||
|
||||
|
||||
RingBuffer(const size_t count) : mBuffer{count} { }
|
||||
|
||||
/** Reset the read and write pointers to zero. This is not thread safe. */
|
||||
|
|
@ -44,13 +44,13 @@ public:
|
|||
* hold the current readable data. If the readable data is in one segment
|
||||
* the second segment has zero length.
|
||||
*/
|
||||
ll_ringbuffer_data_pair getReadVector() const noexcept;
|
||||
DataPair getReadVector() const noexcept;
|
||||
/**
|
||||
* The non-copying data writer. Returns two ringbuffer data pointers that
|
||||
* hold the current writeable data. If the writeable data is in one segment
|
||||
* the second segment has zero length.
|
||||
*/
|
||||
ll_ringbuffer_data_pair getWriteVector() const noexcept;
|
||||
DataPair getWriteVector() const noexcept;
|
||||
|
||||
/**
|
||||
* Return the number of elements available for reading. This is the number
|
||||
|
|
@ -98,6 +98,8 @@ public:
|
|||
void writeAdvance(size_t cnt) noexcept
|
||||
{ mWritePtr.fetch_add(cnt, std::memory_order_acq_rel); }
|
||||
|
||||
size_t getElemSize() const noexcept { return mElemSize; }
|
||||
|
||||
/**
|
||||
* Create a new ringbuffer to hold at least `sz' elements of `elem_sz'
|
||||
* bytes. The number of elements is rounded up to the next power of two
|
||||
|
|
|
|||
|
|
@ -90,30 +90,43 @@ bool semaphore::try_wait() noexcept
|
|||
|
||||
#else
|
||||
|
||||
#if defined(HAVE_PTHREAD_SETNAME_NP) || defined(HAVE_PTHREAD_SET_NAME_NP)
|
||||
#include <pthread.h>
|
||||
#ifdef HAVE_PTHREAD_NP_H
|
||||
#include <pthread_np.h>
|
||||
#endif
|
||||
#include <tuple>
|
||||
|
||||
namespace {
|
||||
|
||||
using setname_t1 = int(*)(const char*);
|
||||
using setname_t2 = int(*)(pthread_t, const char*);
|
||||
using setname_t3 = int(*)(pthread_t, const char*, void*);
|
||||
|
||||
void setname_caller(setname_t1 func, const char *name)
|
||||
{ func(name); }
|
||||
|
||||
void setname_caller(setname_t2 func, const char *name)
|
||||
{ func(pthread_self(), name); }
|
||||
|
||||
void setname_caller(setname_t3 func, const char *name)
|
||||
{ func(pthread_self(), "%s", static_cast<void*>(const_cast<char*>(name))); }
|
||||
|
||||
} // namespace
|
||||
|
||||
void althrd_setname(const char *name)
|
||||
{
|
||||
#if defined(HAVE_PTHREAD_SET_NAME_NP)
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
#elif defined(PTHREAD_SETNAME_NP_ONE_PARAM)
|
||||
pthread_setname_np(name);
|
||||
#elif defined(PTHREAD_SETNAME_NP_THREE_PARAMS)
|
||||
pthread_setname_np(pthread_self(), "%s", (void*)name);
|
||||
#else
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
setname_caller(pthread_set_name_np, name);
|
||||
#elif defined(HAVE_PTHREAD_SETNAME_NP)
|
||||
setname_caller(pthread_setname_np, name);
|
||||
#endif
|
||||
/* Avoid unused function/parameter warnings. */
|
||||
std::ignore = name;
|
||||
std::ignore = static_cast<void(*)(setname_t1,const char*)>(&setname_caller);
|
||||
std::ignore = static_cast<void(*)(setname_t2,const char*)>(&setname_caller);
|
||||
std::ignore = static_cast<void(*)(setname_t3,const char*)>(&setname_caller);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void althrd_setname(const char*) { }
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
namespace al {
|
||||
|
|
|
|||
|
|
@ -35,11 +35,20 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
T normalize()
|
||||
VectorR operator-(const VectorR &rhs) const noexcept
|
||||
{
|
||||
const T length{std::sqrt(mVals[0]*mVals[0] + mVals[1]*mVals[1] + mVals[2]*mVals[2])};
|
||||
if(length > std::numeric_limits<T>::epsilon())
|
||||
const VectorR ret{mVals[0] - rhs.mVals[0], mVals[1] - rhs.mVals[1],
|
||||
mVals[2] - rhs.mVals[2], mVals[3] - rhs.mVals[3]};
|
||||
return ret;
|
||||
}
|
||||
|
||||
T normalize(T limit = std::numeric_limits<T>::epsilon())
|
||||
{
|
||||
limit = std::max(limit, std::numeric_limits<T>::epsilon());
|
||||
const T length_sqr{mVals[0]*mVals[0] + mVals[1]*mVals[1] + mVals[2]*mVals[2]};
|
||||
if(length_sqr > limit*limit)
|
||||
{
|
||||
const T length{std::sqrt(length_sqr)};
|
||||
T inv_length{T{1}/length};
|
||||
mVals[0] *= inv_length;
|
||||
mVals[1] *= inv_length;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue