First Commit
This commit is contained in:
155
externals/openal-soft/common/albit.h
vendored
Normal file
155
externals/openal-soft/common/albit.h
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
#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>
|
||||
#endif
|
||||
|
||||
namespace al {
|
||||
|
||||
#ifdef __BYTE_ORDER__
|
||||
enum class endian {
|
||||
little = __ORDER_LITTLE_ENDIAN__,
|
||||
big = __ORDER_BIG_ENDIAN__,
|
||||
native = __BYTE_ORDER__
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
/* This doesn't support mixed-endian. */
|
||||
namespace detail_ {
|
||||
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) ? true : false;
|
||||
}
|
||||
} // namespace detail_
|
||||
|
||||
enum class endian {
|
||||
big = 0,
|
||||
little = 1,
|
||||
native = detail_::IsLittleEndian() ? little : big
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
/* Define popcount (population count/count 1 bits) and countr_zero (count
|
||||
* trailing zero bits, starting from the lsb) methods, for various integer
|
||||
* types.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
|
||||
namespace detail_ {
|
||||
inline int popcount(unsigned long long val) noexcept { return __builtin_popcountll(val); }
|
||||
inline int popcount(unsigned long val) noexcept { return __builtin_popcountl(val); }
|
||||
inline int popcount(unsigned int val) noexcept { return __builtin_popcount(val); }
|
||||
|
||||
inline int countr_zero(unsigned long long val) noexcept { return __builtin_ctzll(val); }
|
||||
inline int countr_zero(unsigned long val) noexcept { return __builtin_ctzl(val); }
|
||||
inline int countr_zero(unsigned int val) noexcept { return __builtin_ctz(val); }
|
||||
} // namespace detail_
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
||||
int> popcount(T v) noexcept { return detail_::popcount(v); }
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
||||
int> countr_zero(T val) noexcept
|
||||
{ return val ? detail_::countr_zero(val) : std::numeric_limits<T>::digits; }
|
||||
|
||||
#else
|
||||
|
||||
/* There be black magics here. The popcount method is derived from
|
||||
* https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
|
||||
* while the ctz-utilizing-popcount algorithm is shown here
|
||||
* http://www.hackersdelight.org/hdcodetxt/ntz.c.txt
|
||||
* as the ntz2 variant. These likely aren't the most efficient methods, but
|
||||
* 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
|
||||
{
|
||||
T ret{bits};
|
||||
for(size_t i{1};i < sizeof(T);++i)
|
||||
ret = (ret<<8) | bits;
|
||||
return ret;
|
||||
}
|
||||
} // namespace detail_
|
||||
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
||||
int> popcount(T val) noexcept
|
||||
{
|
||||
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)};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
template<typename T>
|
||||
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};
|
||||
_BitScanForward(&idx, static_cast<uint32_t>(v));
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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};
|
||||
#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);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
||||
int> countr_zero(T value)
|
||||
{ return popcount(static_cast<T>(~value & (value - 1))); }
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_BIT_H */
|
||||
17
externals/openal-soft/common/albyte.h
vendored
Normal file
17
externals/openal-soft/common/albyte.h
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef AL_BYTE_H
|
||||
#define AL_BYTE_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
namespace al {
|
||||
|
||||
using byte = unsigned char;
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_BYTE_H */
|
||||
171
externals/openal-soft/common/alcomplex.cpp
vendored
Normal file
171
externals/openal-soft/common/alcomplex.cpp
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "alcomplex.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "albit.h"
|
||||
#include "alnumbers.h"
|
||||
#include "alnumeric.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using ushort = unsigned short;
|
||||
using ushort2 = std::pair<ushort,ushort>;
|
||||
|
||||
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>
|
||||
struct BitReverser {
|
||||
static_assert(N <= sizeof(ushort)*8, "Too many bits for the bit-reversal table.");
|
||||
|
||||
ushort2 mData[BitReverseCounter(N)]{};
|
||||
|
||||
constexpr BitReverser()
|
||||
{
|
||||
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)
|
||||
{
|
||||
mData[ret_i].first = static_cast<ushort>(idx);
|
||||
mData[ret_i].second = static_cast<ushort>(revidx);
|
||||
++ret_i;
|
||||
}
|
||||
}
|
||||
assert(ret_i == al::size(mData));
|
||||
}
|
||||
};
|
||||
|
||||
/* 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 BitReverser<2> BitReverser2{};
|
||||
constexpr BitReverser<3> BitReverser3{};
|
||||
constexpr BitReverser<4> BitReverser4{};
|
||||
constexpr BitReverser<5> BitReverser5{};
|
||||
constexpr BitReverser<6> BitReverser6{};
|
||||
constexpr BitReverser<7> BitReverser7{};
|
||||
constexpr BitReverser<8> BitReverser8{};
|
||||
constexpr BitReverser<9> BitReverser9{};
|
||||
constexpr BitReverser<10> BitReverser10{};
|
||||
constexpr std::array<al::span<const ushort2>,11> gBitReverses{{
|
||||
{}, {},
|
||||
BitReverser2.mData,
|
||||
BitReverser3.mData,
|
||||
BitReverser4.mData,
|
||||
BitReverser5.mData,
|
||||
BitReverser6.mData,
|
||||
BitReverser7.mData,
|
||||
BitReverser8.mData,
|
||||
BitReverser9.mData,
|
||||
BitReverser10.mData
|
||||
}};
|
||||
|
||||
} // namespace
|
||||
|
||||
template<typename Real>
|
||||
std::enable_if_t<std::is_floating_point<Real>::value>
|
||||
complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t<Real> sign)
|
||||
{
|
||||
const size_t fftsize{buffer.size()};
|
||||
/* Get the number of bits used for indexing. Simplifies bit-reversal and
|
||||
* the main loop count.
|
||||
*/
|
||||
const size_t log2_size{static_cast<size_t>(al::countr_zero(fftsize))};
|
||||
|
||||
if(log2_size >= gBitReverses.size()) UNLIKELY
|
||||
{
|
||||
for(size_t idx{1u};idx < fftsize-1;++idx)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
else for(auto &rev : gBitReverses[log2_size])
|
||||
std::swap(buffer[rev.first], buffer[rev.second]);
|
||||
|
||||
/* Iterative form of Danielson-Lanczos lemma */
|
||||
const Real pi{al::numbers::pi_v<Real> * sign};
|
||||
size_t step2{1u};
|
||||
for(size_t i{0};i < log2_size;++i)
|
||||
{
|
||||
const Real arg{pi / static_cast<Real>(step2)};
|
||||
|
||||
/* TODO: Would std::polar(1.0, arg) be any better? */
|
||||
const std::complex<Real> w{std::cos(arg), std::sin(arg)};
|
||||
std::complex<Real> u{1.0, 0.0};
|
||||
const size_t step{step2 << 1};
|
||||
for(size_t j{0};j < step2;j++)
|
||||
{
|
||||
for(size_t k{j};k < fftsize;k+=step)
|
||||
{
|
||||
std::complex<Real> temp{buffer[k+step2] * u};
|
||||
buffer[k+step2] = buffer[k] - temp;
|
||||
buffer[k] += temp;
|
||||
}
|
||||
|
||||
u *= w;
|
||||
}
|
||||
|
||||
step2 <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void complex_hilbert(const al::span<std::complex<double>> buffer)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
|
||||
inverse_fft(buffer);
|
||||
|
||||
const double inverse_size = 1.0/static_cast<double>(buffer.size());
|
||||
auto bufiter = buffer.begin();
|
||||
const auto halfiter = bufiter + (buffer.size()>>1);
|
||||
|
||||
*bufiter *= inverse_size; ++bufiter;
|
||||
bufiter = std::transform(bufiter, halfiter, bufiter,
|
||||
[scale=inverse_size*2.0](std::complex<double> d){ return d * scale; });
|
||||
*bufiter *= inverse_size; ++bufiter;
|
||||
|
||||
std::fill(bufiter, buffer.end(), std::complex<double>{});
|
||||
|
||||
forward_fft(buffer);
|
||||
}
|
||||
|
||||
|
||||
template void complex_fft<>(const al::span<std::complex<float>> buffer, const float sign);
|
||||
template void complex_fft<>(const al::span<std::complex<double>> buffer, const double sign);
|
||||
45
externals/openal-soft/common/alcomplex.h
vendored
Normal file
45
externals/openal-soft/common/alcomplex.h
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef ALCOMPLEX_H
|
||||
#define ALCOMPLEX_H
|
||||
|
||||
#include <complex>
|
||||
#include <type_traits>
|
||||
|
||||
#include "alspan.h"
|
||||
|
||||
/**
|
||||
* Iterative implementation of 2-radix FFT (In-place algorithm). Sign = -1 is
|
||||
* FFT and 1 is inverse FFT. Applies the Discrete Fourier Transform (DFT) to
|
||||
* the data supplied in the buffer, which MUST BE power of two.
|
||||
*/
|
||||
template<typename Real>
|
||||
std::enable_if_t<std::is_floating_point<Real>::value>
|
||||
complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t<Real> sign);
|
||||
|
||||
/**
|
||||
* Calculate the frequency-domain response of the time-domain signal in the
|
||||
* provided buffer, which MUST BE power of two.
|
||||
*/
|
||||
template<typename Real, size_t N>
|
||||
std::enable_if_t<std::is_floating_point<Real>::value>
|
||||
forward_fft(const al::span<std::complex<Real>,N> buffer)
|
||||
{ complex_fft(buffer.subspan(0), -1); }
|
||||
|
||||
/**
|
||||
* Calculate the time-domain signal of the frequency-domain response in the
|
||||
* provided buffer, which MUST BE power of two.
|
||||
*/
|
||||
template<typename Real, size_t N>
|
||||
std::enable_if_t<std::is_floating_point<Real>::value>
|
||||
inverse_fft(const al::span<std::complex<Real>,N> buffer)
|
||||
{ complex_fft(buffer.subspan(0), 1); }
|
||||
|
||||
/**
|
||||
* Calculate the complex helical sequence (discrete-time analytical signal) of
|
||||
* the given input using the discrete Hilbert transform (In-place algorithm).
|
||||
* Fills the buffer with the discrete-time analytical signal stored in the
|
||||
* buffer. The buffer is an array of complex numbers and MUST BE power of two,
|
||||
* and the imaginary components should be cleared to 0.
|
||||
*/
|
||||
void complex_hilbert(const al::span<std::complex<double>> buffer);
|
||||
|
||||
#endif /* ALCOMPLEX_H */
|
||||
16
externals/openal-soft/common/aldeque.h
vendored
Normal file
16
externals/openal-soft/common/aldeque.h
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef ALDEQUE_H
|
||||
#define ALDEQUE_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
namespace al {
|
||||
|
||||
template<typename T>
|
||||
using deque = std::deque<T, al::allocator<T>>;
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* ALDEQUE_H */
|
||||
26
externals/openal-soft/common/alfstream.cpp
vendored
Normal file
26
externals/openal-soft/common/alfstream.cpp
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "alfstream.h"
|
||||
|
||||
#include "strutils.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
namespace al {
|
||||
|
||||
ifstream::ifstream(const char *filename, std::ios_base::openmode mode)
|
||||
: std::ifstream{utf8_to_wstr(filename).c_str(), mode}
|
||||
{ }
|
||||
|
||||
void ifstream::open(const char *filename, std::ios_base::openmode mode)
|
||||
{
|
||||
std::wstring wstr{utf8_to_wstr(filename)};
|
||||
std::ifstream::open(wstr.c_str(), mode);
|
||||
}
|
||||
|
||||
ifstream::~ifstream() = default;
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif
|
||||
45
externals/openal-soft/common/alfstream.h
vendored
Normal file
45
externals/openal-soft/common/alfstream.h
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef AL_FSTREAM_H
|
||||
#define AL_FSTREAM_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
namespace al {
|
||||
|
||||
// Inherit from std::ifstream to accept UTF-8 filenames
|
||||
class ifstream final : public std::ifstream {
|
||||
public:
|
||||
explicit ifstream(const char *filename, std::ios_base::openmode mode=std::ios_base::in);
|
||||
explicit ifstream(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
|
||||
: ifstream{filename.c_str(), mode} { }
|
||||
|
||||
explicit ifstream(const wchar_t *filename, std::ios_base::openmode mode=std::ios_base::in)
|
||||
: std::ifstream{filename, mode} { }
|
||||
explicit ifstream(const std::wstring &filename, std::ios_base::openmode mode=std::ios_base::in)
|
||||
: ifstream{filename.c_str(), mode} { }
|
||||
|
||||
void open(const char *filename, std::ios_base::openmode mode=std::ios_base::in);
|
||||
void open(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
|
||||
{ open(filename.c_str(), mode); }
|
||||
|
||||
~ifstream() override;
|
||||
};
|
||||
|
||||
} // namespace al
|
||||
|
||||
#else /* _WIN32 */
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace al {
|
||||
|
||||
using ifstream = std::ifstream;
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* AL_FSTREAM_H */
|
||||
61
externals/openal-soft/common/almalloc.cpp
vendored
Normal file
61
externals/openal-soft/common/almalloc.cpp
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
|
||||
void *al_malloc(size_t alignment, size_t size)
|
||||
{
|
||||
assert((alignment & (alignment-1)) == 0);
|
||||
alignment = std::max(alignment, alignof(std::max_align_t));
|
||||
|
||||
#if defined(HAVE_POSIX_MEMALIGN)
|
||||
void *ret{};
|
||||
if(posix_memalign(&ret, alignment, size) == 0)
|
||||
return ret;
|
||||
return nullptr;
|
||||
#elif defined(HAVE__ALIGNED_MALLOC)
|
||||
return _aligned_malloc(size, alignment);
|
||||
#else
|
||||
size_t total_size{size + alignment-1 + sizeof(void*)};
|
||||
void *base{std::malloc(total_size)};
|
||||
if(base != nullptr)
|
||||
{
|
||||
void *aligned_ptr{static_cast<char*>(base) + sizeof(void*)};
|
||||
total_size -= sizeof(void*);
|
||||
|
||||
std::align(alignment, size, aligned_ptr, total_size);
|
||||
*(static_cast<void**>(aligned_ptr)-1) = base;
|
||||
base = aligned_ptr;
|
||||
}
|
||||
return base;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *al_calloc(size_t alignment, size_t size)
|
||||
{
|
||||
void *ret{al_malloc(alignment, size)};
|
||||
if(ret) std::memset(ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void al_free(void *ptr) noexcept
|
||||
{
|
||||
#if defined(HAVE_POSIX_MEMALIGN)
|
||||
std::free(ptr);
|
||||
#elif defined(HAVE__ALIGNED_MALLOC)
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
if(ptr != nullptr)
|
||||
std::free(*(static_cast<void**>(ptr) - 1));
|
||||
#endif
|
||||
}
|
||||
311
externals/openal-soft/common/almalloc.h
vendored
Normal file
311
externals/openal-soft/common/almalloc.h
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
#ifndef AL_MALLOC_H
|
||||
#define AL_MALLOC_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "pragmadefs.h"
|
||||
|
||||
|
||||
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() \
|
||||
void *operator new(size_t) = delete; \
|
||||
void *operator new[](size_t) = delete; \
|
||||
void operator delete(void*) noexcept = delete; \
|
||||
void operator delete[](void*) noexcept = delete;
|
||||
|
||||
#define DEF_NEWDEL(T) \
|
||||
void *operator new(size_t size) \
|
||||
{ \
|
||||
static_assert(&operator new == &T::operator new, \
|
||||
"Incorrect container type specified"); \
|
||||
if(void *ret{al_malloc(alignof(T), size)}) \
|
||||
return ret; \
|
||||
throw std::bad_alloc(); \
|
||||
} \
|
||||
void *operator new[](size_t size) { return operator new(size); } \
|
||||
void operator delete(void *block) noexcept { al_free(block); } \
|
||||
void operator delete[](void *block) noexcept { operator delete(block); }
|
||||
|
||||
#define DEF_PLACE_NEWDEL() \
|
||||
void *operator new(size_t /*size*/, void *ptr) noexcept { return ptr; } \
|
||||
void *operator new[](size_t /*size*/, void *ptr) noexcept { return ptr; } \
|
||||
void operator delete(void *block, void*) noexcept { al_free(block); } \
|
||||
void operator delete(void *block) noexcept { al_free(block); } \
|
||||
void operator delete[](void *block, void*) noexcept { al_free(block); } \
|
||||
void operator delete[](void *block) noexcept { al_free(block); }
|
||||
|
||||
enum FamCount : size_t { };
|
||||
|
||||
#define DEF_FAM_NEWDEL(T, FamMem) \
|
||||
static constexpr size_t Sizeof(size_t count) noexcept \
|
||||
{ \
|
||||
static_assert(&Sizeof == &T::Sizeof, \
|
||||
"Incorrect container type specified"); \
|
||||
return std::max(decltype(FamMem)::Sizeof(count, offsetof(T, FamMem)), \
|
||||
sizeof(T)); \
|
||||
} \
|
||||
\
|
||||
void *operator new(size_t /*size*/, FamCount count) \
|
||||
{ \
|
||||
if(void *ret{al_malloc(alignof(T), T::Sizeof(count))}) \
|
||||
return ret; \
|
||||
throw std::bad_alloc(); \
|
||||
} \
|
||||
void *operator new[](size_t /*size*/) = delete; \
|
||||
void operator delete(void *block, FamCount) { al_free(block); } \
|
||||
void operator delete(void *block) noexcept { al_free(block); } \
|
||||
void operator delete[](void* /*block*/) = delete;
|
||||
|
||||
|
||||
namespace al {
|
||||
|
||||
template<typename T, std::size_t Align=alignof(T)>
|
||||
struct allocator {
|
||||
static constexpr std::size_t alignment{std::max(Align, alignof(T))};
|
||||
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using is_always_equal = std::true_type;
|
||||
|
||||
template<typename U>
|
||||
struct rebind {
|
||||
using other = allocator<U, Align>;
|
||||
};
|
||||
|
||||
constexpr explicit allocator() noexcept = default;
|
||||
template<typename U, std::size_t N>
|
||||
constexpr explicit allocator(const allocator<U,N>&) noexcept { }
|
||||
|
||||
T *allocate(std::size_t n)
|
||||
{
|
||||
if(n > std::numeric_limits<std::size_t>::max()/sizeof(T)) throw std::bad_alloc();
|
||||
if(auto p = al_malloc(alignment, n*sizeof(T))) return static_cast<T*>(p);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
void deallocate(T *p, std::size_t) noexcept { al_free(p); }
|
||||
};
|
||||
template<typename T, std::size_t N, typename U, std::size_t M>
|
||||
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>
|
||||
constexpr bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept { return false; }
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr T *to_address(T *p) noexcept
|
||||
{
|
||||
static_assert(!std::is_function<T>::value, "Can't be a function type");
|
||||
return p;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr auto to_address(const T &p) noexcept
|
||||
{ return to_address(p.operator->()); }
|
||||
|
||||
|
||||
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.
|
||||
*/
|
||||
DIAGNOSTIC_PUSH
|
||||
msc_pragma(warning(disable : 4100))
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<!std::is_array<T>::value>
|
||||
destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<T>::value)
|
||||
{ ptr->~T(); }
|
||||
DIAGNOSTIC_POP
|
||||
template<typename T>
|
||||
constexpr std::enable_if_t<std::is_array<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) noexcept(noexcept(al::destroy_at(std::addressof(*first))))
|
||||
{
|
||||
while(first != end)
|
||||
{
|
||||
al::destroy_at(std::addressof(*first));
|
||||
++first;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename N>
|
||||
constexpr std::enable_if_t<std::is_integral<N>::value,T>
|
||||
destroy_n(T first, N count) noexcept(noexcept(al::destroy_at(std::addressof(*first))))
|
||||
{
|
||||
if(count != 0)
|
||||
{
|
||||
do {
|
||||
al::destroy_at(std::addressof(*first));
|
||||
++first;
|
||||
} while(--count);
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename N>
|
||||
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};
|
||||
if(count != 0)
|
||||
{
|
||||
try {
|
||||
do {
|
||||
::new(static_cast<void*>(std::addressof(*current))) ValueT;
|
||||
++current;
|
||||
} while(--count);
|
||||
}
|
||||
catch(...) {
|
||||
al::destroy(first, current);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
/* Storage for flexible array data. This is trivially destructible if type T is
|
||||
* trivially destructible.
|
||||
*/
|
||||
template<typename T, size_t alignment, bool = std::is_trivially_destructible<T>::value>
|
||||
struct FlexArrayStorage {
|
||||
const size_t mSize;
|
||||
union {
|
||||
char mDummy;
|
||||
alignas(alignment) T mArray[1];
|
||||
};
|
||||
|
||||
static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept
|
||||
{
|
||||
const size_t len{sizeof(T)*count};
|
||||
return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base;
|
||||
}
|
||||
|
||||
FlexArrayStorage(size_t size) : mSize{size}
|
||||
{ al::uninitialized_default_construct_n(mArray, mSize); }
|
||||
~FlexArrayStorage() = default;
|
||||
|
||||
FlexArrayStorage(const FlexArrayStorage&) = delete;
|
||||
FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
|
||||
};
|
||||
|
||||
template<typename T, size_t alignment>
|
||||
struct FlexArrayStorage<T,alignment,false> {
|
||||
const size_t mSize;
|
||||
union {
|
||||
char mDummy;
|
||||
alignas(alignment) T mArray[1];
|
||||
};
|
||||
|
||||
static constexpr size_t Sizeof(size_t count, size_t base) noexcept
|
||||
{
|
||||
const size_t len{sizeof(T)*count};
|
||||
return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base;
|
||||
}
|
||||
|
||||
FlexArrayStorage(size_t size) : mSize{size}
|
||||
{ al::uninitialized_default_construct_n(mArray, mSize); }
|
||||
~FlexArrayStorage() { al::destroy_n(mArray, mSize); }
|
||||
|
||||
FlexArrayStorage(const FlexArrayStorage&) = delete;
|
||||
FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
|
||||
};
|
||||
|
||||
/* A flexible array type. Used either standalone or at the end of a parent
|
||||
* struct, with placement new, to have a run-time-sized array that's embedded
|
||||
* with its size.
|
||||
*/
|
||||
template<typename T, size_t alignment=alignof(T)>
|
||||
struct FlexArray {
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv_t<T>;
|
||||
using index_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
using Storage_t_ = FlexArrayStorage<element_type,alignment>;
|
||||
|
||||
Storage_t_ mStore;
|
||||
|
||||
static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept
|
||||
{ return Storage_t_::Sizeof(count, base); }
|
||||
static std::unique_ptr<FlexArray> Create(index_type count)
|
||||
{
|
||||
void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))};
|
||||
return std::unique_ptr<FlexArray>{al::construct_at(static_cast<FlexArray*>(ptr), count)};
|
||||
}
|
||||
|
||||
FlexArray(index_type size) : mStore{size} { }
|
||||
~FlexArray() = default;
|
||||
|
||||
index_type size() const noexcept { return mStore.mSize; }
|
||||
bool empty() const noexcept { return mStore.mSize == 0; }
|
||||
|
||||
pointer data() noexcept { return mStore.mArray; }
|
||||
const_pointer data() const noexcept { return mStore.mArray; }
|
||||
|
||||
reference operator[](index_type i) noexcept { return mStore.mArray[i]; }
|
||||
const_reference operator[](index_type i) const noexcept { return mStore.mArray[i]; }
|
||||
|
||||
reference front() noexcept { return mStore.mArray[0]; }
|
||||
const_reference front() const noexcept { return mStore.mArray[0]; }
|
||||
|
||||
reference back() noexcept { return mStore.mArray[mStore.mSize-1]; }
|
||||
const_reference back() const noexcept { return mStore.mArray[mStore.mSize-1]; }
|
||||
|
||||
iterator begin() noexcept { return mStore.mArray; }
|
||||
const_iterator begin() const noexcept { return mStore.mArray; }
|
||||
const_iterator cbegin() const noexcept { return mStore.mArray; }
|
||||
iterator end() noexcept { return mStore.mArray + mStore.mSize; }
|
||||
const_iterator end() const noexcept { return mStore.mArray + mStore.mSize; }
|
||||
const_iterator cend() const noexcept { return mStore.mArray + mStore.mSize; }
|
||||
|
||||
reverse_iterator rbegin() noexcept { return end(); }
|
||||
const_reverse_iterator rbegin() const noexcept { return end(); }
|
||||
const_reverse_iterator crbegin() const noexcept { return cend(); }
|
||||
reverse_iterator rend() noexcept { return begin(); }
|
||||
const_reverse_iterator rend() const noexcept { return begin(); }
|
||||
const_reverse_iterator crend() const noexcept { return cbegin(); }
|
||||
|
||||
DEF_PLACE_NEWDEL()
|
||||
};
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_MALLOC_H */
|
||||
36
externals/openal-soft/common/alnumbers.h
vendored
Normal file
36
externals/openal-soft/common/alnumbers.h
vendored
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 */
|
||||
308
externals/openal-soft/common/alnumeric.h
vendored
Normal file
308
externals/openal-soft/common/alnumeric.h
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
#ifndef AL_NUMERIC_H
|
||||
#define AL_NUMERIC_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#ifdef HAVE_INTRIN_H
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#ifdef HAVE_SSE_INTRINSICS
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include "altraits.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
inline constexpr int64_t operator "" _i64(unsigned long long int n) noexcept { return static_cast<int64_t>(n); }
|
||||
inline constexpr uint64_t operator "" _u64(unsigned long long int n) noexcept { return static_cast<uint64_t>(n); }
|
||||
|
||||
|
||||
constexpr inline float minf(float a, float b) noexcept
|
||||
{ return ((a > b) ? b : a); }
|
||||
constexpr inline float maxf(float a, float b) noexcept
|
||||
{ return ((a > b) ? a : b); }
|
||||
constexpr inline float clampf(float val, float min, float max) noexcept
|
||||
{ return minf(max, maxf(min, val)); }
|
||||
|
||||
constexpr inline double mind(double a, double b) noexcept
|
||||
{ return ((a > b) ? b : a); }
|
||||
constexpr inline double maxd(double a, double b) noexcept
|
||||
{ return ((a > b) ? a : b); }
|
||||
constexpr inline double clampd(double val, double min, double max) noexcept
|
||||
{ return mind(max, maxd(min, val)); }
|
||||
|
||||
constexpr inline unsigned int minu(unsigned int a, unsigned int b) noexcept
|
||||
{ return ((a > b) ? b : a); }
|
||||
constexpr inline unsigned int maxu(unsigned int a, unsigned int b) noexcept
|
||||
{ return ((a > b) ? a : b); }
|
||||
constexpr inline unsigned int clampu(unsigned int val, unsigned int min, unsigned int max) noexcept
|
||||
{ return minu(max, maxu(min, val)); }
|
||||
|
||||
constexpr inline int mini(int a, int b) noexcept
|
||||
{ return ((a > b) ? b : a); }
|
||||
constexpr inline int maxi(int a, int b) noexcept
|
||||
{ return ((a > b) ? a : b); }
|
||||
constexpr inline int clampi(int val, int min, int max) noexcept
|
||||
{ return mini(max, maxi(min, val)); }
|
||||
|
||||
constexpr inline int64_t mini64(int64_t a, int64_t b) noexcept
|
||||
{ return ((a > b) ? b : a); }
|
||||
constexpr inline int64_t maxi64(int64_t a, int64_t b) noexcept
|
||||
{ return ((a > b) ? a : b); }
|
||||
constexpr inline int64_t clampi64(int64_t val, int64_t min, int64_t max) noexcept
|
||||
{ return mini64(max, maxi64(min, val)); }
|
||||
|
||||
constexpr inline uint64_t minu64(uint64_t a, uint64_t b) noexcept
|
||||
{ return ((a > b) ? b : a); }
|
||||
constexpr inline uint64_t maxu64(uint64_t a, uint64_t b) noexcept
|
||||
{ return ((a > b) ? a : b); }
|
||||
constexpr inline uint64_t clampu64(uint64_t val, uint64_t min, uint64_t max) noexcept
|
||||
{ return minu64(max, maxu64(min, val)); }
|
||||
|
||||
constexpr inline size_t minz(size_t a, size_t b) noexcept
|
||||
{ return ((a > b) ? b : a); }
|
||||
constexpr inline size_t maxz(size_t a, size_t b) noexcept
|
||||
{ return ((a > b) ? a : b); }
|
||||
constexpr inline size_t clampz(size_t val, size_t min, size_t max) noexcept
|
||||
{ return minz(max, maxz(min, val)); }
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
const float mu2{mu*mu}, mu3{mu2*mu};
|
||||
const float a0{-0.5f*mu3 + mu2 + -0.5f*mu};
|
||||
const float a1{ 1.5f*mu3 + -2.5f*mu2 + 1.0f};
|
||||
const float a2{-1.5f*mu3 + 2.0f*mu2 + 0.5f*mu};
|
||||
const float a3{ 0.5f*mu3 + -0.5f*mu2};
|
||||
return val1*a0 + val2*a1 + val3*a2 + val4*a3;
|
||||
}
|
||||
|
||||
|
||||
/** Find the next power-of-2 for non-power-of-2 numbers. */
|
||||
inline uint32_t NextPowerOf2(uint32_t value) noexcept
|
||||
{
|
||||
if(value > 0)
|
||||
{
|
||||
value--;
|
||||
value |= value>>1;
|
||||
value |= value>>2;
|
||||
value |= value>>4;
|
||||
value |= value>>8;
|
||||
value |= value>>16;
|
||||
}
|
||||
return value+1;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the value is not already a multiple of r, round down to the next
|
||||
* multiple.
|
||||
*/
|
||||
template<typename T>
|
||||
constexpr T RoundDown(T value, al::type_identity_t<T> r) noexcept
|
||||
{ return value - (value%r); }
|
||||
|
||||
/**
|
||||
* If the value is not already a multiple of r, round up to the next multiple.
|
||||
*/
|
||||
template<typename T>
|
||||
constexpr T RoundUp(T value, al::type_identity_t<T> r) noexcept
|
||||
{ return RoundDown(value + r-1, r); }
|
||||
|
||||
|
||||
/**
|
||||
* Fast float-to-int conversion. No particular rounding mode is assumed; the
|
||||
* IEEE-754 default is round-to-nearest with ties-to-even, though an app could
|
||||
* change it on its own threads. On some systems, a truncating conversion may
|
||||
* always be the fastest method.
|
||||
*/
|
||||
inline int fastf2i(float f) noexcept
|
||||
{
|
||||
#if defined(HAVE_SSE_INTRINSICS)
|
||||
return _mm_cvt_ss2si(_mm_set_ss(f));
|
||||
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86_FP)
|
||||
|
||||
int i;
|
||||
__asm fld f
|
||||
__asm fistp i
|
||||
return i;
|
||||
|
||||
#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
|
||||
|
||||
int i;
|
||||
#ifdef __SSE_MATH__
|
||||
__asm__("cvtss2si %1, %0" : "=r"(i) : "x"(f));
|
||||
#else
|
||||
__asm__ __volatile__("fistpl %0" : "=m"(i) : "t"(f) : "st");
|
||||
#endif
|
||||
return i;
|
||||
|
||||
#else
|
||||
|
||||
return static_cast<int>(f);
|
||||
#endif
|
||||
}
|
||||
inline unsigned int fastf2u(float f) noexcept
|
||||
{ return static_cast<unsigned int>(fastf2i(f)); }
|
||||
|
||||
/** Converts float-to-int using standard behavior (truncation). */
|
||||
inline int float2int(float f) noexcept
|
||||
{
|
||||
#if defined(HAVE_SSE_INTRINSICS)
|
||||
return _mm_cvtt_ss2si(_mm_set_ss(f));
|
||||
|
||||
#elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0) \
|
||||
|| ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \
|
||||
&& !defined(__SSE_MATH__))
|
||||
int sign, shift, mant;
|
||||
union {
|
||||
float f;
|
||||
int i;
|
||||
} conv;
|
||||
|
||||
conv.f = f;
|
||||
sign = (conv.i>>31) | 1;
|
||||
shift = ((conv.i>>23)&0xff) - (127+23);
|
||||
|
||||
/* Over/underflow */
|
||||
if(shift >= 31 || shift < -23) UNLIKELY
|
||||
return 0;
|
||||
|
||||
mant = (conv.i&0x7fffff) | 0x800000;
|
||||
if(shift < 0) LIKELY
|
||||
return (mant >> -shift) * sign;
|
||||
return (mant << shift) * sign;
|
||||
|
||||
#else
|
||||
|
||||
return static_cast<int>(f);
|
||||
#endif
|
||||
}
|
||||
inline unsigned int float2uint(float f) noexcept
|
||||
{ return static_cast<unsigned int>(float2int(f)); }
|
||||
|
||||
/** Converts double-to-int using standard behavior (truncation). */
|
||||
inline int double2int(double d) noexcept
|
||||
{
|
||||
#if defined(HAVE_SSE_INTRINSICS)
|
||||
return _mm_cvttsd_si32(_mm_set_sd(d));
|
||||
|
||||
#elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2) \
|
||||
|| ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \
|
||||
&& !defined(__SSE2_MATH__))
|
||||
int sign, shift;
|
||||
int64_t mant;
|
||||
union {
|
||||
double d;
|
||||
int64_t i64;
|
||||
} conv;
|
||||
|
||||
conv.d = d;
|
||||
sign = (conv.i64 >> 63) | 1;
|
||||
shift = ((conv.i64 >> 52) & 0x7ff) - (1023 + 52);
|
||||
|
||||
/* Over/underflow */
|
||||
if(shift >= 63 || shift < -52) UNLIKELY
|
||||
return 0;
|
||||
|
||||
mant = (conv.i64 & 0xfffffffffffff_i64) | 0x10000000000000_i64;
|
||||
if(shift < 0) LIKELY
|
||||
return (int)(mant >> -shift) * sign;
|
||||
return (int)(mant << shift) * sign;
|
||||
|
||||
#else
|
||||
|
||||
return static_cast<int>(d);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a float to the nearest integral value, according to the current
|
||||
* rounding mode. This is essentially an inlined version of rintf, although
|
||||
* makes fewer promises (e.g. -0 or -0.25 rounded to 0 may result in +0).
|
||||
*/
|
||||
inline float fast_roundf(float f) noexcept
|
||||
{
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \
|
||||
&& !defined(__SSE_MATH__)
|
||||
|
||||
float out;
|
||||
__asm__ __volatile__("frndint" : "=t"(out) : "0"(f));
|
||||
return out;
|
||||
|
||||
#elif (defined(__GNUC__) || defined(__clang__)) && defined(__aarch64__)
|
||||
|
||||
float out;
|
||||
__asm__ volatile("frintx %s0, %s1" : "=w"(out) : "w"(f));
|
||||
return out;
|
||||
|
||||
#else
|
||||
|
||||
/* Integral limit, where sub-integral precision is not available for
|
||||
* floats.
|
||||
*/
|
||||
static const float ilim[2]{
|
||||
8388608.0f /* 0x1.0p+23 */,
|
||||
-8388608.0f /* -0x1.0p+23 */
|
||||
};
|
||||
unsigned int sign, expo;
|
||||
union {
|
||||
float f;
|
||||
unsigned int i;
|
||||
} conv;
|
||||
|
||||
conv.f = f;
|
||||
sign = (conv.i>>31)&0x01;
|
||||
expo = (conv.i>>23)&0xff;
|
||||
|
||||
if(expo >= 150/*+23*/) UNLIKELY
|
||||
{
|
||||
/* An exponent (base-2) of 23 or higher is incapable of sub-integral
|
||||
* precision, so it's already an integral value. We don't need to worry
|
||||
* about infinity or NaN here.
|
||||
*/
|
||||
return f;
|
||||
}
|
||||
/* Adding the integral limit to the value (with a matching sign) forces a
|
||||
* result that has no sub-integral precision, and is consequently forced to
|
||||
* round to an integral value. Removing the integral limit then restores
|
||||
* the initial value rounded to the integral. The compiler should not
|
||||
* optimize this out because of non-associative rules on floating-point
|
||||
* math (as long as you don't use -fassociative-math,
|
||||
* -funsafe-math-optimizations, -ffast-math, or -Ofast, in which case this
|
||||
* may break).
|
||||
*/
|
||||
f += ilim[sign];
|
||||
return f - ilim[sign];
|
||||
#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 */
|
||||
353
externals/openal-soft/common/aloptional.h
vendored
Normal file
353
externals/openal-soft/common/aloptional.h
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
#ifndef AL_OPTIONAL_H
|
||||
#define AL_OPTIONAL_H
|
||||
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "almalloc.h"
|
||||
|
||||
namespace al {
|
||||
|
||||
struct nullopt_t { };
|
||||
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;
|
||||
|
||||
/* 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*>()
|
||||
|
||||
/* 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, 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 = detail_::optional_storage<T>;
|
||||
|
||||
storage_t mStore{};
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
constexpr optional() = default;
|
||||
constexpr optional(const optional&) = default;
|
||||
constexpr optional(optional&&) = default;
|
||||
constexpr optional(nullopt_t) noexcept { }
|
||||
template<typename ...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;
|
||||
|
||||
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>
|
||||
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(mStore.mHasValue)
|
||||
mStore.mValue = std::forward<U>(rhs);
|
||||
else
|
||||
mStore.construct(std::forward<U>(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
constexpr explicit operator bool() const noexcept { return mStore.mHasValue; }
|
||||
constexpr bool has_value() const noexcept { return mStore.mHasValue; }
|
||||
|
||||
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>
|
||||
constexpr T value_or(U&& defval) const&
|
||||
{ return bool(*this) ? **this : static_cast<T>(std::forward<U>(defval)); }
|
||||
template<typename U>
|
||||
constexpr T value_or(U&& defval) &&
|
||||
{ return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr T& emplace(Args&& ...args)
|
||||
{
|
||||
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>
|
||||
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>
|
||||
constexpr optional<T> make_optional(Args&& ...args)
|
||||
{ return optional<T>{in_place, std::forward<Args>(args)...}; }
|
||||
|
||||
template<typename T, typename U, typename... 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 */
|
||||
354
externals/openal-soft/common/alspan.h
vendored
Normal file
354
externals/openal-soft/common/alspan.h
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
#ifndef AL_SPAN_H
|
||||
#define AL_SPAN_H
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "altraits.h"
|
||||
|
||||
namespace al {
|
||||
|
||||
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(const T (&)[N]) noexcept
|
||||
{ return N; }
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr auto data(T &cont) noexcept(noexcept(cont.data())) -> decltype(cont.data())
|
||||
{ return cont.data(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr auto data(const T &cont) noexcept(noexcept(cont.data())) -> decltype(cont.data())
|
||||
{ return cont.data(); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
constexpr T* data(T (&arr)[N]) noexcept
|
||||
{ return arr; }
|
||||
|
||||
template<typename T>
|
||||
constexpr const T* data(std::initializer_list<T> list) noexcept
|
||||
{ return list.begin(); }
|
||||
|
||||
|
||||
constexpr size_t dynamic_extent{static_cast<size_t>(-1)};
|
||||
|
||||
template<typename T, size_t E=dynamic_extent>
|
||||
class span;
|
||||
|
||||
namespace detail_ {
|
||||
template<typename... Ts>
|
||||
using void_t = void;
|
||||
|
||||
template<typename T>
|
||||
struct is_span_ : std::false_type { };
|
||||
template<typename T, size_t E>
|
||||
struct is_span_<span<T,E>> : std::true_type { };
|
||||
template<typename T>
|
||||
constexpr bool is_span_v = is_span_<std::remove_cv_t<T>>::value;
|
||||
|
||||
template<typename T>
|
||||
struct is_std_array_ : std::false_type { };
|
||||
template<typename T, size_t N>
|
||||
struct is_std_array_<std::array<T,N>> : std::true_type { };
|
||||
template<typename T>
|
||||
constexpr bool is_std_array_v = is_std_array_<std::remove_cv_t<T>>::value;
|
||||
|
||||
template<typename T, typename = void>
|
||||
constexpr bool has_size_and_data = false;
|
||||
template<typename T>
|
||||
constexpr bool has_size_and_data<T,
|
||||
void_t<decltype(al::size(std::declval<T>())), decltype(al::data(std::declval<T>()))>>
|
||||
= true;
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool is_array_compatible = std::is_convertible<T(*)[],U(*)[]>::value;
|
||||
|
||||
template<typename C, typename T>
|
||||
constexpr bool is_valid_container = !is_span_v<C> && !is_std_array_v<C>
|
||||
&& !std::is_array<C>::value && has_size_and_data<C>
|
||||
&& is_array_compatible<std::remove_pointer_t<decltype(al::data(std::declval<C&>()))>,T>;
|
||||
} // namespace detail_
|
||||
|
||||
#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
|
||||
|
||||
template<typename T, size_t E>
|
||||
class span {
|
||||
public:
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv_t<T>;
|
||||
using index_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
static constexpr size_t extent{E};
|
||||
|
||||
template<bool is0=(extent == 0), REQUIRES(is0)>
|
||||
constexpr span() noexcept { }
|
||||
template<typename U>
|
||||
constexpr explicit span(U iter, index_type) : mData{to_address(iter)} { }
|
||||
template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)>
|
||||
constexpr explicit span(U first, V) : mData{to_address(first)} { }
|
||||
|
||||
constexpr span(type_identity_t<element_type> (&arr)[E]) noexcept
|
||||
: span{al::data(arr), al::size(arr)}
|
||||
{ }
|
||||
constexpr span(std::array<value_type,E> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
|
||||
template<typename U=T, REQUIRES(std::is_const<U>::value)>
|
||||
constexpr span(const std::array<value_type,E> &arr) noexcept
|
||||
: span{al::data(arr), al::size(arr)}
|
||||
{ }
|
||||
|
||||
template<typename U, REQUIRES(detail_::is_valid_container<U, element_type>)>
|
||||
constexpr explicit span(U&& cont) : span{al::data(cont), al::size(cont)} { }
|
||||
|
||||
template<typename U, index_type N, REQUIRES(!std::is_same<element_type,U>::value
|
||||
&& detail_::is_array_compatible<U,element_type> && N == dynamic_extent)>
|
||||
constexpr explicit span(const span<U,N> &span_) noexcept
|
||||
: span{al::data(span_), al::size(span_)}
|
||||
{ }
|
||||
template<typename U, index_type N, REQUIRES(!std::is_same<element_type,U>::value
|
||||
&& detail_::is_array_compatible<U,element_type> && N == extent)>
|
||||
constexpr span(const span<U,N> &span_) noexcept : span{al::data(span_), al::size(span_)} { }
|
||||
constexpr span(const span&) noexcept = default;
|
||||
|
||||
constexpr span& operator=(const span &rhs) noexcept = default;
|
||||
|
||||
constexpr reference front() const { return *mData; }
|
||||
constexpr reference back() const { return *(mData+E-1); }
|
||||
constexpr reference operator[](index_type idx) const { return mData[idx]; }
|
||||
constexpr pointer data() const noexcept { return mData; }
|
||||
|
||||
constexpr index_type size() const noexcept { return E; }
|
||||
constexpr index_type size_bytes() const noexcept { return E * sizeof(value_type); }
|
||||
constexpr bool empty() const noexcept { return E == 0; }
|
||||
|
||||
constexpr iterator begin() const noexcept { return mData; }
|
||||
constexpr iterator end() const noexcept { return mData+E; }
|
||||
constexpr const_iterator cbegin() const noexcept { return mData; }
|
||||
constexpr const_iterator cend() const noexcept { return mData+E; }
|
||||
|
||||
constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
|
||||
constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
|
||||
constexpr const_reverse_iterator crbegin() const noexcept
|
||||
{ return const_reverse_iterator{cend()}; }
|
||||
constexpr const_reverse_iterator crend() const noexcept
|
||||
{ return const_reverse_iterator{cbegin()}; }
|
||||
|
||||
template<size_t C>
|
||||
constexpr span<element_type,C> first() const
|
||||
{
|
||||
static_assert(E >= C, "New size exceeds original capacity");
|
||||
return span<element_type,C>{mData, C};
|
||||
}
|
||||
|
||||
template<size_t C>
|
||||
constexpr span<element_type,C> last() const
|
||||
{
|
||||
static_assert(E >= C, "New size exceeds original capacity");
|
||||
return span<element_type,C>{mData+(E-C), C};
|
||||
}
|
||||
|
||||
template<size_t O, size_t C>
|
||||
constexpr auto subspan() const -> std::enable_if_t<C!=dynamic_extent,span<element_type,C>>
|
||||
{
|
||||
static_assert(E >= O, "Offset exceeds extent");
|
||||
static_assert(E-O >= C, "New size exceeds original capacity");
|
||||
return span<element_type,C>{mData+O, C};
|
||||
}
|
||||
|
||||
template<size_t O, size_t C=dynamic_extent>
|
||||
constexpr auto subspan() const -> std::enable_if_t<C==dynamic_extent,span<element_type,E-O>>
|
||||
{
|
||||
static_assert(E >= O, "Offset exceeds extent");
|
||||
return span<element_type,E-O>{mData+O, E-O};
|
||||
}
|
||||
|
||||
/* NOTE: Can't declare objects of a specialized template class prior to
|
||||
* defining the specialization. As a result, these methods need to be
|
||||
* defined later.
|
||||
*/
|
||||
constexpr span<element_type,dynamic_extent> first(size_t count) const;
|
||||
constexpr span<element_type,dynamic_extent> last(size_t count) const;
|
||||
constexpr span<element_type,dynamic_extent> subspan(size_t offset,
|
||||
size_t count=dynamic_extent) const;
|
||||
|
||||
private:
|
||||
pointer mData{nullptr};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class span<T,dynamic_extent> {
|
||||
public:
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv_t<T>;
|
||||
using index_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
static constexpr size_t extent{dynamic_extent};
|
||||
|
||||
constexpr span() noexcept = default;
|
||||
template<typename U>
|
||||
constexpr span(U iter, index_type count)
|
||||
: mData{to_address(iter)}, mDataEnd{to_address(iter)+count}
|
||||
{ }
|
||||
template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)>
|
||||
constexpr span(U first, V last) : span{to_address(first), static_cast<size_t>(last-first)}
|
||||
{ }
|
||||
|
||||
template<size_t N>
|
||||
constexpr span(type_identity_t<element_type> (&arr)[N]) noexcept
|
||||
: span{al::data(arr), al::size(arr)}
|
||||
{ }
|
||||
template<size_t N>
|
||||
constexpr span(std::array<value_type,N> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
|
||||
template<size_t N, typename U=T, REQUIRES(std::is_const<U>::value)>
|
||||
constexpr span(const std::array<value_type,N> &arr) noexcept
|
||||
: span{al::data(arr), al::size(arr)}
|
||||
{ }
|
||||
|
||||
template<typename U, REQUIRES(detail_::is_valid_container<U, element_type>)>
|
||||
constexpr span(U&& cont) : span{al::data(cont), al::size(cont)} { }
|
||||
|
||||
template<typename U, size_t N, REQUIRES((!std::is_same<element_type,U>::value || extent != N)
|
||||
&& detail_::is_array_compatible<U,element_type>)>
|
||||
constexpr span(const span<U,N> &span_) noexcept : span{al::data(span_), al::size(span_)} { }
|
||||
constexpr span(const span&) noexcept = default;
|
||||
|
||||
constexpr span& operator=(const span &rhs) noexcept = default;
|
||||
|
||||
constexpr reference front() const { return *mData; }
|
||||
constexpr reference back() const { return *(mDataEnd-1); }
|
||||
constexpr reference operator[](index_type idx) const { return mData[idx]; }
|
||||
constexpr pointer data() const noexcept { return mData; }
|
||||
|
||||
constexpr index_type size() const noexcept { return static_cast<index_type>(mDataEnd-mData); }
|
||||
constexpr index_type size_bytes() const noexcept
|
||||
{ return static_cast<index_type>(mDataEnd-mData) * sizeof(value_type); }
|
||||
constexpr bool empty() const noexcept { return mData == mDataEnd; }
|
||||
|
||||
constexpr iterator begin() const noexcept { return mData; }
|
||||
constexpr iterator end() const noexcept { return mDataEnd; }
|
||||
constexpr const_iterator cbegin() const noexcept { return mData; }
|
||||
constexpr const_iterator cend() const noexcept { return mDataEnd; }
|
||||
|
||||
constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
|
||||
constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
|
||||
constexpr const_reverse_iterator crbegin() const noexcept
|
||||
{ return const_reverse_iterator{cend()}; }
|
||||
constexpr const_reverse_iterator crend() const noexcept
|
||||
{ return const_reverse_iterator{cbegin()}; }
|
||||
|
||||
template<size_t C>
|
||||
constexpr span<element_type,C> first() const
|
||||
{ return span<element_type,C>{mData, C}; }
|
||||
|
||||
constexpr span first(size_t count) const
|
||||
{ return (count >= size()) ? *this : span{mData, mData+count}; }
|
||||
|
||||
template<size_t C>
|
||||
constexpr span<element_type,C> last() const
|
||||
{ return span<element_type,C>{mDataEnd-C, C}; }
|
||||
|
||||
constexpr span last(size_t count) const
|
||||
{ return (count >= size()) ? *this : span{mDataEnd-count, mDataEnd}; }
|
||||
|
||||
template<size_t O, size_t C>
|
||||
constexpr auto subspan() const -> std::enable_if_t<C!=dynamic_extent,span<element_type,C>>
|
||||
{ return span<element_type,C>{mData+O, C}; }
|
||||
|
||||
template<size_t O, size_t C=dynamic_extent>
|
||||
constexpr auto subspan() const -> std::enable_if_t<C==dynamic_extent,span<element_type,C>>
|
||||
{ return span<element_type,C>{mData+O, mDataEnd}; }
|
||||
|
||||
constexpr span subspan(size_t offset, size_t count=dynamic_extent) const
|
||||
{
|
||||
return (offset > size()) ? span{} :
|
||||
(count >= size()-offset) ? span{mData+offset, mDataEnd} :
|
||||
span{mData+offset, mData+offset+count};
|
||||
}
|
||||
|
||||
private:
|
||||
pointer mData{nullptr};
|
||||
pointer mDataEnd{nullptr};
|
||||
};
|
||||
|
||||
template<typename T, size_t E>
|
||||
constexpr inline auto span<T,E>::first(size_t count) const -> span<element_type,dynamic_extent>
|
||||
{
|
||||
return (count >= size()) ? span<element_type>{mData, extent} :
|
||||
span<element_type>{mData, count};
|
||||
}
|
||||
|
||||
template<typename T, size_t E>
|
||||
constexpr inline auto span<T,E>::last(size_t count) const -> span<element_type,dynamic_extent>
|
||||
{
|
||||
return (count >= size()) ? span<element_type>{mData, extent} :
|
||||
span<element_type>{mData+extent-count, count};
|
||||
}
|
||||
|
||||
template<typename T, size_t E>
|
||||
constexpr inline auto span<T,E>::subspan(size_t offset, size_t count) const
|
||||
-> span<element_type,dynamic_extent>
|
||||
{
|
||||
return (offset > size()) ? span<element_type>{} :
|
||||
(count >= size()-offset) ? span<element_type>{mData+offset, mData+extent} :
|
||||
span<element_type>{mData+offset, mData+offset+count};
|
||||
}
|
||||
|
||||
/* Helpers to deal with the lack of user-defined deduction guides (C++17). */
|
||||
template<typename T, typename U>
|
||||
constexpr auto as_span(T ptr, U count_or_end)
|
||||
{
|
||||
using value_type = typename std::pointer_traits<T>::element_type;
|
||||
return span<value_type>{ptr, count_or_end};
|
||||
}
|
||||
template<typename T, size_t N>
|
||||
constexpr auto as_span(T (&arr)[N]) noexcept { return span<T,N>{al::data(arr), al::size(arr)}; }
|
||||
template<typename T, size_t N>
|
||||
constexpr auto as_span(std::array<T,N> &arr) noexcept
|
||||
{ return span<T,N>{al::data(arr), al::size(arr)}; }
|
||||
template<typename T, size_t N>
|
||||
constexpr auto as_span(const std::array<T,N> &arr) noexcept
|
||||
{ return span<std::add_const_t<T>,N>{al::data(arr), al::size(arr)}; }
|
||||
template<typename U, REQUIRES(!detail_::is_span_v<U> && !detail_::is_std_array_v<U>
|
||||
&& !std::is_array<U>::value && detail_::has_size_and_data<U>)>
|
||||
constexpr auto as_span(U&& cont)
|
||||
{
|
||||
using value_type = std::remove_pointer_t<decltype(al::data(std::declval<U&>()))>;
|
||||
return span<value_type>{al::data(cont), al::size(cont)};
|
||||
}
|
||||
template<typename T, size_t N>
|
||||
constexpr auto as_span(span<T,N> span_) noexcept { return span_; }
|
||||
|
||||
#undef REQUIRES
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_SPAN_H */
|
||||
45
externals/openal-soft/common/alstring.cpp
vendored
Normal file
45
externals/openal-soft/common/alstring.cpp
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "alstring.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
int to_upper(const char ch)
|
||||
{
|
||||
using char8_traits = std::char_traits<char>;
|
||||
return std::toupper(char8_traits::to_int_type(ch));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace al {
|
||||
|
||||
int strcasecmp(const char *str0, const char *str1) noexcept
|
||||
{
|
||||
do {
|
||||
const int diff{to_upper(*str0) - to_upper(*str1)};
|
||||
if(diff < 0) return -1;
|
||||
if(diff > 0) return 1;
|
||||
} while(*(str0++) && *(str1++));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int strncasecmp(const char *str0, const char *str1, std::size_t len) noexcept
|
||||
{
|
||||
if(len > 0)
|
||||
{
|
||||
do {
|
||||
const int diff{to_upper(*str0) - to_upper(*str1)};
|
||||
if(diff < 0) return -1;
|
||||
if(diff > 0) return 1;
|
||||
} while(--len && *(str0++) && *(str1++));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace al
|
||||
18
externals/openal-soft/common/alstring.h
vendored
Normal file
18
externals/openal-soft/common/alstring.h
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef AL_STRING_H
|
||||
#define AL_STRING_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace al {
|
||||
|
||||
/* These would be better served by using a string_view-like span/view with
|
||||
* case-insensitive char traits.
|
||||
*/
|
||||
int strcasecmp(const char *str0, const char *str1) noexcept;
|
||||
int strncasecmp(const char *str0, const char *str1, std::size_t len) noexcept;
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_STRING_H */
|
||||
14
externals/openal-soft/common/altraits.h
vendored
Normal file
14
externals/openal-soft/common/altraits.h
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef COMMON_ALTRAITS_H
|
||||
#define COMMON_ALTRAITS_H
|
||||
|
||||
namespace al {
|
||||
|
||||
template<typename T>
|
||||
struct type_identity { using type = T; };
|
||||
|
||||
template<typename T>
|
||||
using type_identity_t = typename type_identity<T>::type;
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* COMMON_ALTRAITS_H */
|
||||
33
externals/openal-soft/common/atomic.h
vendored
Normal file
33
externals/openal-soft/common/atomic.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef AL_ATOMIC_H
|
||||
#define AL_ATOMIC_H
|
||||
|
||||
#include <atomic>
|
||||
|
||||
|
||||
using RefCount = std::atomic<unsigned int>;
|
||||
|
||||
inline void InitRef(RefCount &ref, unsigned int value)
|
||||
{ ref.store(value, std::memory_order_relaxed); }
|
||||
inline unsigned int ReadRef(RefCount &ref)
|
||||
{ return ref.load(std::memory_order_acquire); }
|
||||
inline unsigned int IncrementRef(RefCount &ref)
|
||||
{ return ref.fetch_add(1u, std::memory_order_acq_rel)+1u; }
|
||||
inline unsigned int DecrementRef(RefCount &ref)
|
||||
{ return ref.fetch_sub(1u, std::memory_order_acq_rel)-1u; }
|
||||
|
||||
|
||||
/* WARNING: A livelock is theoretically possible if another thread keeps
|
||||
* changing the head without giving this a chance to actually swap in the new
|
||||
* one (practically impossible with this little code, but...).
|
||||
*/
|
||||
template<typename T>
|
||||
inline void AtomicReplaceHead(std::atomic<T> &head, T newhead)
|
||||
{
|
||||
T first_ = head.load(std::memory_order_acquire);
|
||||
do {
|
||||
newhead->next.store(first_, std::memory_order_relaxed);
|
||||
} while(!head.compare_exchange_weak(first_, newhead,
|
||||
std::memory_order_acq_rel, std::memory_order_acquire));
|
||||
}
|
||||
|
||||
#endif /* AL_ATOMIC_H */
|
||||
68
externals/openal-soft/common/comptr.h
vendored
Normal file
68
externals/openal-soft/common/comptr.h
vendored
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(&rhs != this) LIKELY
|
||||
{
|
||||
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
|
||||
44
externals/openal-soft/common/dynload.cpp
vendored
Normal file
44
externals/openal-soft/common/dynload.cpp
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "dynload.h"
|
||||
|
||||
#include "strutils.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
void *LoadLib(const char *name)
|
||||
{
|
||||
std::wstring wname{utf8_to_wstr(name)};
|
||||
return LoadLibraryW(wname.c_str());
|
||||
}
|
||||
void CloseLib(void *handle)
|
||||
{ FreeLibrary(static_cast<HMODULE>(handle)); }
|
||||
void *GetSymbol(void *handle, const char *name)
|
||||
{ return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name)); }
|
||||
|
||||
#elif defined(HAVE_DLFCN_H)
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
void *LoadLib(const char *name)
|
||||
{
|
||||
dlerror();
|
||||
void *handle{dlopen(name, RTLD_NOW)};
|
||||
const char *err{dlerror()};
|
||||
if(err) handle = nullptr;
|
||||
return handle;
|
||||
}
|
||||
void CloseLib(void *handle)
|
||||
{ dlclose(handle); }
|
||||
void *GetSymbol(void *handle, const char *name)
|
||||
{
|
||||
dlerror();
|
||||
void *sym{dlsym(handle, name)};
|
||||
const char *err{dlerror()};
|
||||
if(err) sym = nullptr;
|
||||
return sym;
|
||||
}
|
||||
#endif
|
||||
14
externals/openal-soft/common/dynload.h
vendored
Normal file
14
externals/openal-soft/common/dynload.h
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef AL_DYNLOAD_H
|
||||
#define AL_DYNLOAD_H
|
||||
|
||||
#if defined(_WIN32) || defined(HAVE_DLFCN_H)
|
||||
|
||||
#define HAVE_DYNLOAD
|
||||
|
||||
void *LoadLib(const char *name);
|
||||
void CloseLib(void *handle);
|
||||
void *GetSymbol(void *handle, const char *name);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* AL_DYNLOAD_H */
|
||||
120
externals/openal-soft/common/intrusive_ptr.h
vendored
Normal file
120
externals/openal-soft/common/intrusive_ptr.h
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef INTRUSIVE_PTR_H
|
||||
#define INTRUSIVE_PTR_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "atomic.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
namespace al {
|
||||
|
||||
template<typename T>
|
||||
class intrusive_ref {
|
||||
RefCount mRef{1u};
|
||||
|
||||
public:
|
||||
unsigned int add_ref() noexcept { return IncrementRef(mRef); }
|
||||
unsigned int dec_ref() noexcept
|
||||
{
|
||||
auto ref = DecrementRef(mRef);
|
||||
if(ref == 0) UNLIKELY
|
||||
delete static_cast<T*>(this);
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release only if doing so would not bring the object to 0 references and
|
||||
* delete it. Returns false if the object could not be released.
|
||||
*
|
||||
* NOTE: The caller is responsible for handling a failed release, as it
|
||||
* means the object has no other references and needs to be be deleted
|
||||
* somehow.
|
||||
*/
|
||||
bool releaseIfNoDelete() noexcept
|
||||
{
|
||||
auto val = mRef.load(std::memory_order_acquire);
|
||||
while(val > 1 && !mRef.compare_exchange_strong(val, val-1, std::memory_order_acq_rel))
|
||||
{
|
||||
/* val was updated with the current value on failure, so just try
|
||||
* again.
|
||||
*/
|
||||
}
|
||||
|
||||
return val >= 2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
class intrusive_ptr {
|
||||
T *mPtr{nullptr};
|
||||
|
||||
public:
|
||||
intrusive_ptr() noexcept = default;
|
||||
intrusive_ptr(const intrusive_ptr &rhs) noexcept : mPtr{rhs.mPtr}
|
||||
{ if(mPtr) mPtr->add_ref(); }
|
||||
intrusive_ptr(intrusive_ptr&& rhs) noexcept : mPtr{rhs.mPtr}
|
||||
{ rhs.mPtr = nullptr; }
|
||||
intrusive_ptr(std::nullptr_t) noexcept { }
|
||||
explicit intrusive_ptr(T *ptr) noexcept : mPtr{ptr} { }
|
||||
~intrusive_ptr() { if(mPtr) mPtr->dec_ref(); }
|
||||
|
||||
intrusive_ptr& operator=(const intrusive_ptr &rhs) noexcept
|
||||
{
|
||||
static_assert(noexcept(std::declval<T*>()->dec_ref()), "dec_ref must be noexcept");
|
||||
|
||||
if(rhs.mPtr) rhs.mPtr->add_ref();
|
||||
if(mPtr) mPtr->dec_ref();
|
||||
mPtr = rhs.mPtr;
|
||||
return *this;
|
||||
}
|
||||
intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept
|
||||
{
|
||||
if(&rhs != this) LIKELY
|
||||
{
|
||||
if(mPtr) mPtr->dec_ref();
|
||||
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; }
|
||||
|
||||
void reset(T *ptr=nullptr) noexcept
|
||||
{
|
||||
if(mPtr)
|
||||
mPtr->dec_ref();
|
||||
mPtr = ptr;
|
||||
}
|
||||
|
||||
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); }
|
||||
};
|
||||
|
||||
#define AL_DECL_OP(op) \
|
||||
template<typename T> \
|
||||
inline bool operator op(const intrusive_ptr<T> &lhs, const T *rhs) noexcept \
|
||||
{ return lhs.get() op rhs; } \
|
||||
template<typename T> \
|
||||
inline bool operator op(const T *lhs, const intrusive_ptr<T> &rhs) noexcept \
|
||||
{ return lhs op rhs.get(); }
|
||||
|
||||
AL_DECL_OP(==)
|
||||
AL_DECL_OP(!=)
|
||||
AL_DECL_OP(<=)
|
||||
AL_DECL_OP(>=)
|
||||
AL_DECL_OP(<)
|
||||
AL_DECL_OP(>)
|
||||
|
||||
#undef AL_DECL_OP
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* INTRUSIVE_PTR_H */
|
||||
93
externals/openal-soft/common/opthelpers.h
vendored
Normal file
93
externals/openal-soft/common/opthelpers.h
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
#ifndef OPTHELPERS_H
|
||||
#define OPTHELPERS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
#ifdef __has_builtin
|
||||
#define HAS_BUILTIN __has_builtin
|
||||
#else
|
||||
#define HAS_BUILTIN(x) (0)
|
||||
#endif
|
||||
|
||||
#ifdef __has_cpp_attribute
|
||||
#define HAS_ATTRIBUTE __has_cpp_attribute
|
||||
#else
|
||||
#define HAS_ATTRIBUTE(x) (0)
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define force_inline [[gnu::always_inline]] inline
|
||||
#elif defined(_MSC_VER)
|
||||
#define force_inline __forceinline
|
||||
#else
|
||||
#define force_inline inline
|
||||
#endif
|
||||
|
||||
/* Unlike the likely attribute, ASSUME requires the condition to be true or
|
||||
* else it invokes undefined behavior. It's essentially an assert without
|
||||
* actually checking the condition at run-time, allowing for stronger
|
||||
* optimizations than the likely attribute.
|
||||
*/
|
||||
#if HAS_BUILTIN(__builtin_assume)
|
||||
#define ASSUME __builtin_assume
|
||||
#elif defined(_MSC_VER)
|
||||
#define ASSUME __assume
|
||||
#elif __has_attribute(assume)
|
||||
#define ASSUME(x) [[assume(x)]]
|
||||
#elif HAS_BUILTIN(__builtin_unreachable)
|
||||
#define ASSUME(x) do { if(x) break; __builtin_unreachable(); } while(0)
|
||||
#else
|
||||
#define ASSUME(x) ((void)0)
|
||||
#endif
|
||||
|
||||
/* This shouldn't be needed since unknown attributes are ignored, but older
|
||||
* versions of GCC choke on the attribute syntax in certain situations.
|
||||
*/
|
||||
#if HAS_ATTRIBUTE(likely)
|
||||
#define LIKELY [[likely]]
|
||||
#define UNLIKELY [[unlikely]]
|
||||
#else
|
||||
#define LIKELY
|
||||
#define UNLIKELY
|
||||
#endif
|
||||
|
||||
namespace al {
|
||||
|
||||
template<typename T>
|
||||
constexpr std::underlying_type_t<T> to_underlying(T e) noexcept
|
||||
{ return static_cast<std::underlying_type_t<T>>(e); }
|
||||
|
||||
[[noreturn]] inline void unreachable()
|
||||
{
|
||||
#if HAS_BUILTIN(__builtin_unreachable)
|
||||
__builtin_unreachable();
|
||||
#else
|
||||
ASSUME(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
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 HAS_BUILTIN(__builtin_assume_aligned)
|
||||
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
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* OPTHELPERS_H */
|
||||
214
externals/openal-soft/common/phase_shifter.h
vendored
Normal file
214
externals/openal-soft/common/phase_shifter.h
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
#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(al::as_span(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(al::as_span(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;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#endif /* PHASE_SHIFTER_H */
|
||||
222
externals/openal-soft/common/polyphase_resampler.cpp
vendored
Normal file
222
externals/openal-soft/common/polyphase_resampler.cpp
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
|
||||
#include "polyphase_resampler.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "alnumbers.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr double Epsilon{1e-9};
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
/* This is the normalized cardinal sine (sinc) function.
|
||||
*
|
||||
* sinc(x) = { 1, x = 0
|
||||
* { sin(pi x) / (pi x), otherwise.
|
||||
*/
|
||||
double Sinc(const double x)
|
||||
{
|
||||
if(std::abs(x) < Epsilon) UNLIKELY
|
||||
return 1.0;
|
||||
return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
|
||||
}
|
||||
|
||||
/* The zero-order modified Bessel function of the first kind, used for the
|
||||
* Kaiser window.
|
||||
*
|
||||
* I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
|
||||
* = sum_{k=0}^inf ((x / 2)^k / k!)^2
|
||||
*/
|
||||
constexpr double BesselI_0(const double x)
|
||||
{
|
||||
// Start at k=1 since k=0 is trivial.
|
||||
const double x2{x/2.0};
|
||||
double term{1.0};
|
||||
double sum{1.0};
|
||||
int k{1};
|
||||
|
||||
// Let the integration converge until the term of the sum is no longer
|
||||
// significant.
|
||||
double last_sum{};
|
||||
do {
|
||||
const double y{x2 / k};
|
||||
++k;
|
||||
last_sum = sum;
|
||||
term *= y * y;
|
||||
sum += term;
|
||||
} while(sum != last_sum);
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* Calculate a Kaiser window from the given beta value and a normalized k
|
||||
* [-1, 1].
|
||||
*
|
||||
* w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
|
||||
* { 0, elsewhere.
|
||||
*
|
||||
* Where k can be calculated as:
|
||||
*
|
||||
* k = i / l, where -l <= i <= l.
|
||||
*
|
||||
* or:
|
||||
*
|
||||
* k = 2 i / M - 1, where 0 <= i <= M.
|
||||
*/
|
||||
double Kaiser(const double b, const double k)
|
||||
{
|
||||
if(!(k >= -1.0 && k <= 1.0))
|
||||
return 0.0;
|
||||
return BesselI_0(b * std::sqrt(1.0 - k*k)) / BesselI_0(b);
|
||||
}
|
||||
|
||||
// Calculates the greatest common divisor of a and b.
|
||||
constexpr uint Gcd(uint x, uint y)
|
||||
{
|
||||
while(y > 0)
|
||||
{
|
||||
const uint z{y};
|
||||
y = x % y;
|
||||
x = z;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Calculates the size (order) of the Kaiser window. Rejection is in dB and
|
||||
* the transition width is normalized frequency (0.5 is nyquist).
|
||||
*
|
||||
* M = { ceil((r - 7.95) / (2.285 2 pi f_t)), r > 21
|
||||
* { ceil(5.79 / 2 pi f_t), r <= 21.
|
||||
*
|
||||
*/
|
||||
constexpr uint CalcKaiserOrder(const double rejection, const double transition)
|
||||
{
|
||||
const double w_t{2.0 * al::numbers::pi * transition};
|
||||
if(rejection > 21.0) LIKELY
|
||||
return static_cast<uint>(std::ceil((rejection - 7.95) / (2.285 * w_t)));
|
||||
return static_cast<uint>(std::ceil(5.79 / w_t));
|
||||
}
|
||||
|
||||
// Calculates the beta value of the Kaiser window. Rejection is in dB.
|
||||
constexpr double CalcKaiserBeta(const double rejection)
|
||||
{
|
||||
if(rejection > 50.0) LIKELY
|
||||
return 0.1102 * (rejection - 8.7);
|
||||
if(rejection >= 21.0)
|
||||
return (0.5842 * std::pow(rejection - 21.0, 0.4)) +
|
||||
(0.07886 * (rejection - 21.0));
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* Calculates a point on the Kaiser-windowed sinc filter for the given half-
|
||||
* width, beta, gain, and cutoff. The point is specified in non-normalized
|
||||
* samples, from 0 to M, where M = (2 l + 1).
|
||||
*
|
||||
* w(k) 2 p f_t sinc(2 f_t x)
|
||||
*
|
||||
* x -- centered sample index (i - l)
|
||||
* k -- normalized and centered window index (x / l)
|
||||
* w(k) -- window function (Kaiser)
|
||||
* p -- gain compensation factor when sampling
|
||||
* f_t -- normalized center frequency (or cutoff; 0.5 is nyquist)
|
||||
*/
|
||||
double SincFilter(const uint l, const double b, const double gain, const double cutoff,
|
||||
const uint i)
|
||||
{
|
||||
const double x{static_cast<double>(i) - l};
|
||||
return Kaiser(b, x / l) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * x);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Calculate the resampling metrics and build the Kaiser-windowed sinc filter
|
||||
// that's used to cut frequencies above the destination nyquist.
|
||||
void PPhaseResampler::init(const uint srcRate, const uint dstRate)
|
||||
{
|
||||
const uint gcd{Gcd(srcRate, dstRate)};
|
||||
mP = dstRate / gcd;
|
||||
mQ = srcRate / gcd;
|
||||
|
||||
/* The cutoff is adjusted by half the transition width, so the transition
|
||||
* ends before the nyquist (0.5). Both are scaled by the downsampling
|
||||
* factor.
|
||||
*/
|
||||
double cutoff, width;
|
||||
if(mP > mQ)
|
||||
{
|
||||
cutoff = 0.475 / mP;
|
||||
width = 0.05 / mP;
|
||||
}
|
||||
else
|
||||
{
|
||||
cutoff = 0.475 / mQ;
|
||||
width = 0.05 / mQ;
|
||||
}
|
||||
// A rejection of -180 dB is used for the stop band. Round up when
|
||||
// calculating the left offset to avoid increasing the transition width.
|
||||
const uint l{(CalcKaiserOrder(180.0, width)+1) / 2};
|
||||
const double beta{CalcKaiserBeta(180.0)};
|
||||
mM = l*2 + 1;
|
||||
mL = l;
|
||||
mF.resize(mM);
|
||||
for(uint i{0};i < mM;i++)
|
||||
mF[i] = SincFilter(l, beta, mP, cutoff, i);
|
||||
}
|
||||
|
||||
// Perform the upsample-filter-downsample resampling operation using a
|
||||
// polyphase filter implementation.
|
||||
void PPhaseResampler::process(const uint inN, const double *in, const uint outN, double *out)
|
||||
{
|
||||
if(outN == 0) UNLIKELY
|
||||
return;
|
||||
|
||||
// Handle in-place operation.
|
||||
std::vector<double> workspace;
|
||||
double *work{out};
|
||||
if(work == in) UNLIKELY
|
||||
{
|
||||
workspace.resize(outN);
|
||||
work = workspace.data();
|
||||
}
|
||||
|
||||
// Resample the input.
|
||||
const uint p{mP}, q{mQ}, m{mM}, l{mL};
|
||||
const double *f{mF.data()};
|
||||
for(uint i{0};i < outN;i++)
|
||||
{
|
||||
// Input starts at l to compensate for the filter delay. This will
|
||||
// drop any build-up from the first half of the filter.
|
||||
size_t j_f{(l + q*i) % p};
|
||||
size_t j_s{(l + q*i) / p};
|
||||
|
||||
// Only take input when 0 <= j_s < inN.
|
||||
double r{0.0};
|
||||
if(j_f < m) LIKELY
|
||||
{
|
||||
size_t filt_len{(m-j_f+p-1) / p};
|
||||
if(j_s+1 > inN) LIKELY
|
||||
{
|
||||
size_t skip{std::min<size_t>(j_s+1 - inN, filt_len)};
|
||||
j_f += p*skip;
|
||||
j_s -= skip;
|
||||
filt_len -= skip;
|
||||
}
|
||||
if(size_t todo{std::min<size_t>(j_s+1, filt_len)}) LIKELY
|
||||
{
|
||||
do {
|
||||
r += f[j_f] * in[j_s];
|
||||
j_f += p;
|
||||
--j_s;
|
||||
} while(--todo);
|
||||
}
|
||||
}
|
||||
work[i] = r;
|
||||
}
|
||||
// Clean up after in-place operation.
|
||||
if(work != out)
|
||||
std::copy_n(work, outN, out);
|
||||
}
|
||||
47
externals/openal-soft/common/polyphase_resampler.h
vendored
Normal file
47
externals/openal-soft/common/polyphase_resampler.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef POLYPHASE_RESAMPLER_H
|
||||
#define POLYPHASE_RESAMPLER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
/* This is a polyphase sinc-filtered resampler. It is built for very high
|
||||
* quality results, rather than real-time performance.
|
||||
*
|
||||
* Upsample Downsample
|
||||
*
|
||||
* p/q = 3/2 p/q = 3/5
|
||||
*
|
||||
* M-+-+-+-> M-+-+-+->
|
||||
* -------------------+ ---------------------+
|
||||
* p s * f f f f|f| | p s * f f f f f |
|
||||
* | 0 * 0 0 0|0|0 | | 0 * 0 0 0 0|0| |
|
||||
* v 0 * 0 0|0|0 0 | v 0 * 0 0 0|0|0 |
|
||||
* s * f|f|f f f | s * f f|f|f f |
|
||||
* 0 * |0|0 0 0 0 | 0 * 0|0|0 0 0 |
|
||||
* --------+=+--------+ 0 * |0|0 0 0 0 |
|
||||
* d . d .|d|. d . d ----------+=+--------+
|
||||
* d . . . .|d|. . . .
|
||||
* q->
|
||||
* q-+-+-+->
|
||||
*
|
||||
* P_f(i,j) = q i mod p + pj
|
||||
* P_s(i,j) = floor(q i / p) - j
|
||||
* d[i=0..N-1] = sum_{j=0}^{floor((M - 1) / p)} {
|
||||
* { f[P_f(i,j)] s[P_s(i,j)], P_f(i,j) < M
|
||||
* { 0, P_f(i,j) >= M. }
|
||||
*/
|
||||
|
||||
struct PPhaseResampler {
|
||||
void init(const uint srcRate, const uint dstRate);
|
||||
void process(const uint inN, const double *in, const uint outN, double *out);
|
||||
|
||||
explicit operator bool() const noexcept { return !mF.empty(); }
|
||||
|
||||
private:
|
||||
uint mP, mQ, mM, mL;
|
||||
std::vector<double> mF;
|
||||
};
|
||||
|
||||
#endif /* POLYPHASE_RESAMPLER_H */
|
||||
21
externals/openal-soft/common/pragmadefs.h
vendored
Normal file
21
externals/openal-soft/common/pragmadefs.h
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef PRAGMADEFS_H
|
||||
#define PRAGMADEFS_H
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define DIAGNOSTIC_PUSH __pragma(warning(push))
|
||||
#define DIAGNOSTIC_POP __pragma(warning(pop))
|
||||
#define std_pragma(...)
|
||||
#define msc_pragma __pragma
|
||||
#else
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
|
||||
#define DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define DIAGNOSTIC_PUSH
|
||||
#define DIAGNOSTIC_POP
|
||||
#endif
|
||||
#define std_pragma _Pragma
|
||||
#define msc_pragma(...)
|
||||
#endif
|
||||
|
||||
#endif /* PRAGMADEFS_H */
|
||||
224
externals/openal-soft/common/ringbuffer.cpp
vendored
Normal file
224
externals/openal-soft/common/ringbuffer.cpp
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
RingBufferPtr RingBuffer::Create(size_t sz, size_t elem_sz, int limit_writes)
|
||||
{
|
||||
size_t power_of_two{0u};
|
||||
if(sz > 0)
|
||||
{
|
||||
power_of_two = sz;
|
||||
power_of_two |= power_of_two>>1;
|
||||
power_of_two |= power_of_two>>2;
|
||||
power_of_two |= power_of_two>>4;
|
||||
power_of_two |= power_of_two>>8;
|
||||
power_of_two |= power_of_two>>16;
|
||||
#if SIZE_MAX > UINT_MAX
|
||||
power_of_two |= power_of_two>>32;
|
||||
#endif
|
||||
}
|
||||
++power_of_two;
|
||||
if(power_of_two <= sz || power_of_two > std::numeric_limits<size_t>::max()/elem_sz)
|
||||
throw std::overflow_error{"Ring buffer size overflow"};
|
||||
|
||||
const size_t bufbytes{power_of_two * elem_sz};
|
||||
RingBufferPtr rb{new(FamCount(bufbytes)) RingBuffer{bufbytes}};
|
||||
rb->mWriteSize = limit_writes ? sz : (power_of_two-1);
|
||||
rb->mSizeMask = power_of_two - 1;
|
||||
rb->mElemSize = elem_sz;
|
||||
|
||||
return rb;
|
||||
}
|
||||
|
||||
void RingBuffer::reset() noexcept
|
||||
{
|
||||
mWritePtr.store(0, std::memory_order_relaxed);
|
||||
mReadPtr.store(0, std::memory_order_relaxed);
|
||||
std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, al::byte{});
|
||||
}
|
||||
|
||||
|
||||
size_t RingBuffer::read(void *dest, size_t cnt) noexcept
|
||||
{
|
||||
const size_t free_cnt{readSpace()};
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
const size_t to_read{std::min(cnt, free_cnt)};
|
||||
size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
|
||||
|
||||
size_t n1, n2;
|
||||
const size_t cnt2{read_ptr + to_read};
|
||||
if(cnt2 > mSizeMask+1)
|
||||
{
|
||||
n1 = mSizeMask+1 - read_ptr;
|
||||
n2 = cnt2 & mSizeMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_read;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize,
|
||||
static_cast<al::byte*>(dest));
|
||||
read_ptr += n1;
|
||||
if(n2 > 0)
|
||||
{
|
||||
std::copy_n(mBuffer.begin(), n2*mElemSize, outiter);
|
||||
read_ptr += n2;
|
||||
}
|
||||
mReadPtr.store(read_ptr, std::memory_order_release);
|
||||
return to_read;
|
||||
}
|
||||
|
||||
size_t RingBuffer::peek(void *dest, size_t cnt) const noexcept
|
||||
{
|
||||
const size_t free_cnt{readSpace()};
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
const size_t to_read{std::min(cnt, free_cnt)};
|
||||
size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
|
||||
|
||||
size_t n1, n2;
|
||||
const size_t cnt2{read_ptr + to_read};
|
||||
if(cnt2 > mSizeMask+1)
|
||||
{
|
||||
n1 = mSizeMask+1 - read_ptr;
|
||||
n2 = cnt2 & mSizeMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_read;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize,
|
||||
static_cast<al::byte*>(dest));
|
||||
if(n2 > 0)
|
||||
std::copy_n(mBuffer.begin(), n2*mElemSize, outiter);
|
||||
return to_read;
|
||||
}
|
||||
|
||||
size_t RingBuffer::write(const void *src, size_t cnt) noexcept
|
||||
{
|
||||
const size_t free_cnt{writeSpace()};
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
const size_t to_write{std::min(cnt, free_cnt)};
|
||||
size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask};
|
||||
|
||||
size_t n1, n2;
|
||||
const size_t cnt2{write_ptr + to_write};
|
||||
if(cnt2 > mSizeMask+1)
|
||||
{
|
||||
n1 = mSizeMask+1 - write_ptr;
|
||||
n2 = cnt2 & mSizeMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_write;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
auto srcbytes = static_cast<const al::byte*>(src);
|
||||
std::copy_n(srcbytes, n1*mElemSize, mBuffer.begin() + write_ptr*mElemSize);
|
||||
write_ptr += n1;
|
||||
if(n2 > 0)
|
||||
{
|
||||
std::copy_n(srcbytes + n1*mElemSize, n2*mElemSize, mBuffer.begin());
|
||||
write_ptr += n2;
|
||||
}
|
||||
mWritePtr.store(write_ptr, std::memory_order_release);
|
||||
return to_write;
|
||||
}
|
||||
|
||||
|
||||
auto RingBuffer::getReadVector() const noexcept -> DataPair
|
||||
{
|
||||
DataPair ret;
|
||||
|
||||
size_t w{mWritePtr.load(std::memory_order_acquire)};
|
||||
size_t r{mReadPtr.load(std::memory_order_acquire)};
|
||||
w &= mSizeMask;
|
||||
r &= mSizeMask;
|
||||
const size_t free_cnt{(w-r) & mSizeMask};
|
||||
|
||||
const size_t cnt2{r + free_cnt};
|
||||
if(cnt2 > mSizeMask+1)
|
||||
{
|
||||
/* Two part vector: the rest of the buffer after the current read ptr,
|
||||
* plus some from the start of the buffer. */
|
||||
ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize);
|
||||
ret.first.len = mSizeMask+1 - r;
|
||||
ret.second.buf = const_cast<al::byte*>(mBuffer.data());
|
||||
ret.second.len = cnt2 & mSizeMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Single part vector: just the rest of the buffer */
|
||||
ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize);
|
||||
ret.first.len = free_cnt;
|
||||
ret.second.buf = nullptr;
|
||||
ret.second.len = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto RingBuffer::getWriteVector() const noexcept -> DataPair
|
||||
{
|
||||
DataPair ret;
|
||||
|
||||
size_t w{mWritePtr.load(std::memory_order_acquire)};
|
||||
size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask};
|
||||
w &= mSizeMask;
|
||||
r &= mSizeMask;
|
||||
const size_t free_cnt{(r-w-1) & mSizeMask};
|
||||
|
||||
const size_t cnt2{w + free_cnt};
|
||||
if(cnt2 > mSizeMask+1)
|
||||
{
|
||||
/* Two part vector: the rest of the buffer after the current write ptr,
|
||||
* plus some from the start of the buffer. */
|
||||
ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize);
|
||||
ret.first.len = mSizeMask+1 - w;
|
||||
ret.second.buf = const_cast<al::byte*>(mBuffer.data());
|
||||
ret.second.len = cnt2 & mSizeMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize);
|
||||
ret.first.len = free_cnt;
|
||||
ret.second.buf = nullptr;
|
||||
ret.second.len = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
115
externals/openal-soft/common/ringbuffer.h
vendored
Normal file
115
externals/openal-soft/common/ringbuffer.h
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef RINGBUFFER_H
|
||||
#define RINGBUFFER_H
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
#include <utility>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
|
||||
* to include an element size. Consequently, parameters and return values for a
|
||||
* size or count is in 'elements', not bytes. Additionally, it only supports
|
||||
* single-consumer/single-provider operation.
|
||||
*/
|
||||
|
||||
struct RingBuffer {
|
||||
private:
|
||||
std::atomic<size_t> mWritePtr{0u};
|
||||
std::atomic<size_t> mReadPtr{0u};
|
||||
size_t mWriteSize{0u};
|
||||
size_t mSizeMask{0u};
|
||||
size_t mElemSize{0u};
|
||||
|
||||
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. */
|
||||
void reset() noexcept;
|
||||
|
||||
/**
|
||||
* The non-copying data reader. Returns two ringbuffer data pointers that
|
||||
* hold the current readable data. If the readable data is in one segment
|
||||
* the second segment has zero length.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
DataPair getWriteVector() const noexcept;
|
||||
|
||||
/**
|
||||
* Return the number of elements available for reading. This is the number
|
||||
* of elements in front of the read pointer and behind the write pointer.
|
||||
*/
|
||||
size_t readSpace() const noexcept
|
||||
{
|
||||
const size_t w{mWritePtr.load(std::memory_order_acquire)};
|
||||
const size_t r{mReadPtr.load(std::memory_order_acquire)};
|
||||
return (w-r) & mSizeMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* The copying data reader. Copy at most `cnt' elements into `dest'.
|
||||
* Returns the actual number of elements copied.
|
||||
*/
|
||||
size_t read(void *dest, size_t cnt) noexcept;
|
||||
/**
|
||||
* The copying data reader w/o read pointer advance. Copy at most `cnt'
|
||||
* elements into `dest'. Returns the actual number of elements copied.
|
||||
*/
|
||||
size_t peek(void *dest, size_t cnt) const noexcept;
|
||||
/** Advance the read pointer `cnt' places. */
|
||||
void readAdvance(size_t cnt) noexcept
|
||||
{ mReadPtr.fetch_add(cnt, std::memory_order_acq_rel); }
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of elements available for writing. This is the number
|
||||
* of elements in front of the write pointer and behind the read pointer.
|
||||
*/
|
||||
size_t writeSpace() const noexcept
|
||||
{
|
||||
const size_t w{mWritePtr.load(std::memory_order_acquire)};
|
||||
const size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask};
|
||||
return (r-w-1) & mSizeMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* The copying data writer. Copy at most `cnt' elements from `src'. Returns
|
||||
* the actual number of elements copied.
|
||||
*/
|
||||
size_t write(const void *src, size_t cnt) noexcept;
|
||||
/** Advance the write pointer `cnt' places. */
|
||||
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
|
||||
* (even if it is already a power of two, to ensure the requested amount
|
||||
* can be written).
|
||||
*/
|
||||
static std::unique_ptr<RingBuffer> Create(size_t sz, size_t elem_sz, int limit_writes);
|
||||
|
||||
DEF_FAM_NEWDEL(RingBuffer, mBuffer)
|
||||
};
|
||||
using RingBufferPtr = std::unique_ptr<RingBuffer>;
|
||||
|
||||
#endif /* RINGBUFFER_H */
|
||||
64
externals/openal-soft/common/strutils.cpp
vendored
Normal file
64
externals/openal-soft/common/strutils.cpp
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "strutils.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
std::string wstr_to_utf8(const WCHAR *wstr)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
|
||||
if(len > 0)
|
||||
{
|
||||
ret.resize(len);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &ret[0], len, nullptr, nullptr);
|
||||
ret.pop_back();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::wstring utf8_to_wstr(const char *str)
|
||||
{
|
||||
std::wstring ret;
|
||||
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0);
|
||||
if(len > 0)
|
||||
{
|
||||
ret.resize(len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str, -1, &ret[0], len);
|
||||
ret.pop_back();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace al {
|
||||
|
||||
al::optional<std::string> getenv(const char *envname)
|
||||
{
|
||||
const char *str{std::getenv(envname)};
|
||||
if(str && str[0] != '\0')
|
||||
return str;
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
al::optional<std::wstring> getenv(const WCHAR *envname)
|
||||
{
|
||||
const WCHAR *str{_wgetenv(envname)};
|
||||
if(str && str[0] != L'\0')
|
||||
return str;
|
||||
return al::nullopt;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace al
|
||||
24
externals/openal-soft/common/strutils.h
vendored
Normal file
24
externals/openal-soft/common/strutils.h
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef AL_STRUTILS_H
|
||||
#define AL_STRUTILS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "aloptional.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wchar.h>
|
||||
|
||||
std::string wstr_to_utf8(const wchar_t *wstr);
|
||||
std::wstring utf8_to_wstr(const char *str);
|
||||
#endif
|
||||
|
||||
namespace al {
|
||||
|
||||
al::optional<std::string> getenv(const char *envname);
|
||||
#ifdef _WIN32
|
||||
al::optional<std::wstring> getenv(const wchar_t *envname);
|
||||
#endif
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_STRUTILS_H */
|
||||
197
externals/openal-soft/common/threads.cpp
vendored
Normal file
197
externals/openal-soft/common/threads.cpp
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "opthelpers.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include <system_error>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
void althrd_setname(const char *name)
|
||||
{
|
||||
#if defined(_MSC_VER) && !defined(_M_ARM)
|
||||
|
||||
#define MS_VC_EXCEPTION 0x406D1388
|
||||
#pragma pack(push,8)
|
||||
struct {
|
||||
DWORD dwType; // Must be 0x1000.
|
||||
LPCSTR szName; // Pointer to name (in user addr space).
|
||||
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
||||
DWORD dwFlags; // Reserved for future use, must be zero.
|
||||
} info;
|
||||
#pragma pack(pop)
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name;
|
||||
info.dwThreadID = ~DWORD{0};
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try {
|
||||
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
|
||||
}
|
||||
__except(EXCEPTION_CONTINUE_EXECUTION) {
|
||||
}
|
||||
#undef MS_VC_EXCEPTION
|
||||
|
||||
#else
|
||||
|
||||
(void)name;
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace al {
|
||||
|
||||
semaphore::semaphore(unsigned int initial)
|
||||
{
|
||||
if(initial > static_cast<unsigned int>(std::numeric_limits<int>::max()))
|
||||
throw std::system_error(std::make_error_code(std::errc::value_too_large));
|
||||
mSem = CreateSemaphore(nullptr, initial, std::numeric_limits<int>::max(), nullptr);
|
||||
if(mSem == nullptr)
|
||||
throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again));
|
||||
}
|
||||
|
||||
semaphore::~semaphore()
|
||||
{ CloseHandle(mSem); }
|
||||
|
||||
void semaphore::post()
|
||||
{
|
||||
if(!ReleaseSemaphore(static_cast<HANDLE>(mSem), 1, nullptr))
|
||||
throw std::system_error(std::make_error_code(std::errc::value_too_large));
|
||||
}
|
||||
|
||||
void semaphore::wait() noexcept
|
||||
{ WaitForSingleObject(static_cast<HANDLE>(mSem), INFINITE); }
|
||||
|
||||
bool semaphore::try_wait() noexcept
|
||||
{ return WaitForSingleObject(static_cast<HANDLE>(mSem), 0) == WAIT_OBJECT_0; }
|
||||
|
||||
} // namespace al
|
||||
|
||||
#else
|
||||
|
||||
#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 = void(*)(pthread_t, const char*);
|
||||
using setname_t4 = 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(), name); }
|
||||
|
||||
void setname_caller(setname_t4 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)
|
||||
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);
|
||||
std::ignore = static_cast<void(*)(setname_t4,const char*)>(&setname_caller);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
namespace al {
|
||||
|
||||
semaphore::semaphore(unsigned int initial)
|
||||
{
|
||||
mSem = dispatch_semaphore_create(initial);
|
||||
if(!mSem)
|
||||
throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again));
|
||||
}
|
||||
|
||||
semaphore::~semaphore()
|
||||
{ dispatch_release(mSem); }
|
||||
|
||||
void semaphore::post()
|
||||
{ dispatch_semaphore_signal(mSem); }
|
||||
|
||||
void semaphore::wait() noexcept
|
||||
{ dispatch_semaphore_wait(mSem, DISPATCH_TIME_FOREVER); }
|
||||
|
||||
bool semaphore::try_wait() noexcept
|
||||
{ return dispatch_semaphore_wait(mSem, DISPATCH_TIME_NOW) == 0; }
|
||||
|
||||
} // namespace al
|
||||
|
||||
#else /* !__APPLE__ */
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
namespace al {
|
||||
|
||||
semaphore::semaphore(unsigned int initial)
|
||||
{
|
||||
if(sem_init(&mSem, 0, initial) != 0)
|
||||
throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again));
|
||||
}
|
||||
|
||||
semaphore::~semaphore()
|
||||
{ sem_destroy(&mSem); }
|
||||
|
||||
void semaphore::post()
|
||||
{
|
||||
if(sem_post(&mSem) != 0)
|
||||
throw std::system_error(std::make_error_code(std::errc::value_too_large));
|
||||
}
|
||||
|
||||
void semaphore::wait() noexcept
|
||||
{
|
||||
while(sem_wait(&mSem) == -1 && errno == EINTR) {
|
||||
}
|
||||
}
|
||||
|
||||
bool semaphore::try_wait() noexcept
|
||||
{ return sem_trywait(&mSem) == 0; }
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#endif /* _WIN32 */
|
||||
48
externals/openal-soft/common/threads.h
vendored
Normal file
48
externals/openal-soft/common/threads.h
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef AL_THREADS_H
|
||||
#define AL_THREADS_H
|
||||
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
/* force_align_arg_pointer is required for proper function arguments aligning
|
||||
* when SSE code is used. Some systems (Windows, QNX) do not guarantee our
|
||||
* thread functions will be properly aligned on the stack, even though GCC may
|
||||
* generate code with the assumption that it is. */
|
||||
#define FORCE_ALIGN __attribute__((force_align_arg_pointer))
|
||||
#else
|
||||
#define FORCE_ALIGN
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <dispatch/dispatch.h>
|
||||
#elif !defined(_WIN32)
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
void althrd_setname(const char *name);
|
||||
|
||||
namespace al {
|
||||
|
||||
class semaphore {
|
||||
#ifdef _WIN32
|
||||
using native_type = void*;
|
||||
#elif defined(__APPLE__)
|
||||
using native_type = dispatch_semaphore_t;
|
||||
#else
|
||||
using native_type = sem_t;
|
||||
#endif
|
||||
native_type mSem;
|
||||
|
||||
public:
|
||||
semaphore(unsigned int initial=0);
|
||||
semaphore(const semaphore&) = delete;
|
||||
~semaphore();
|
||||
|
||||
semaphore& operator=(const semaphore&) = delete;
|
||||
|
||||
void post();
|
||||
void wait() noexcept;
|
||||
bool try_wait() noexcept;
|
||||
};
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_THREADS_H */
|
||||
120
externals/openal-soft/common/vecmat.h
vendored
Normal file
120
externals/openal-soft/common/vecmat.h
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef COMMON_VECMAT_H
|
||||
#define COMMON_VECMAT_H
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
|
||||
#include "alspan.h"
|
||||
|
||||
|
||||
namespace alu {
|
||||
|
||||
template<typename T>
|
||||
class VectorR {
|
||||
static_assert(std::is_floating_point<T>::value, "Must use floating-point types");
|
||||
alignas(16) T mVals[4];
|
||||
|
||||
public:
|
||||
constexpr VectorR() noexcept = default;
|
||||
constexpr VectorR(const VectorR&) noexcept = default;
|
||||
constexpr explicit VectorR(T a, T b, T c, T d) noexcept : mVals{a, b, c, d} { }
|
||||
|
||||
constexpr VectorR& operator=(const VectorR&) noexcept = default;
|
||||
|
||||
constexpr T& operator[](size_t idx) noexcept { return mVals[idx]; }
|
||||
constexpr const T& operator[](size_t idx) const noexcept { return mVals[idx]; }
|
||||
|
||||
constexpr VectorR& operator+=(const VectorR &rhs) noexcept
|
||||
{
|
||||
mVals[0] += rhs.mVals[0];
|
||||
mVals[1] += rhs.mVals[1];
|
||||
mVals[2] += rhs.mVals[2];
|
||||
mVals[3] += rhs.mVals[3];
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr VectorR operator-(const VectorR &rhs) const noexcept
|
||||
{
|
||||
return VectorR{mVals[0] - rhs.mVals[0], mVals[1] - rhs.mVals[1],
|
||||
mVals[2] - rhs.mVals[2], mVals[3] - rhs.mVals[3]};
|
||||
}
|
||||
|
||||
constexpr 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;
|
||||
mVals[2] *= inv_length;
|
||||
return length;
|
||||
}
|
||||
mVals[0] = mVals[1] = mVals[2] = T{0};
|
||||
return T{0};
|
||||
}
|
||||
|
||||
constexpr VectorR cross_product(const alu::VectorR<T> &rhs) const noexcept
|
||||
{
|
||||
return VectorR{
|
||||
mVals[1]*rhs.mVals[2] - mVals[2]*rhs.mVals[1],
|
||||
mVals[2]*rhs.mVals[0] - mVals[0]*rhs.mVals[2],
|
||||
mVals[0]*rhs.mVals[1] - mVals[1]*rhs.mVals[0],
|
||||
T{0}};
|
||||
}
|
||||
|
||||
constexpr T dot_product(const alu::VectorR<T> &rhs) const noexcept
|
||||
{ return mVals[0]*rhs.mVals[0] + mVals[1]*rhs.mVals[1] + mVals[2]*rhs.mVals[2]; }
|
||||
};
|
||||
using Vector = VectorR<float>;
|
||||
|
||||
template<typename T>
|
||||
class MatrixR {
|
||||
static_assert(std::is_floating_point<T>::value, "Must use floating-point types");
|
||||
alignas(16) T mVals[16];
|
||||
|
||||
public:
|
||||
constexpr MatrixR() noexcept = default;
|
||||
constexpr MatrixR(const MatrixR&) noexcept = default;
|
||||
constexpr explicit MatrixR(
|
||||
T aa, T ab, T ac, T ad,
|
||||
T ba, T bb, T bc, T bd,
|
||||
T ca, T cb, T cc, T cd,
|
||||
T da, T db, T dc, T dd) noexcept
|
||||
: mVals{aa,ab,ac,ad, ba,bb,bc,bd, ca,cb,cc,cd, da,db,dc,dd}
|
||||
{ }
|
||||
|
||||
constexpr MatrixR& operator=(const MatrixR&) noexcept = default;
|
||||
|
||||
constexpr auto operator[](size_t idx) noexcept { return al::span<T,4>{&mVals[idx*4], 4}; }
|
||||
constexpr auto operator[](size_t idx) const noexcept
|
||||
{ return al::span<const T,4>{&mVals[idx*4], 4}; }
|
||||
|
||||
static constexpr MatrixR Identity() noexcept
|
||||
{
|
||||
return MatrixR{
|
||||
T{1}, T{0}, T{0}, T{0},
|
||||
T{0}, T{1}, T{0}, T{0},
|
||||
T{0}, T{0}, T{1}, T{0},
|
||||
T{0}, T{0}, T{0}, T{1}};
|
||||
}
|
||||
};
|
||||
using Matrix = MatrixR<float>;
|
||||
|
||||
template<typename T>
|
||||
constexpr VectorR<T> operator*(const MatrixR<T> &mtx, const VectorR<T> &vec) noexcept
|
||||
{
|
||||
return VectorR<T>{
|
||||
vec[0]*mtx[0][0] + vec[1]*mtx[1][0] + vec[2]*mtx[2][0] + vec[3]*mtx[3][0],
|
||||
vec[0]*mtx[0][1] + vec[1]*mtx[1][1] + vec[2]*mtx[2][1] + vec[3]*mtx[3][1],
|
||||
vec[0]*mtx[0][2] + vec[1]*mtx[1][2] + vec[2]*mtx[2][2] + vec[3]*mtx[3][2],
|
||||
vec[0]*mtx[0][3] + vec[1]*mtx[1][3] + vec[2]*mtx[2][3] + vec[3]*mtx[3][3]};
|
||||
}
|
||||
|
||||
} // namespace alu
|
||||
|
||||
#endif /* COMMON_VECMAT_H */
|
||||
15
externals/openal-soft/common/vector.h
vendored
Normal file
15
externals/openal-soft/common/vector.h
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef AL_VECTOR_H
|
||||
#define AL_VECTOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "almalloc.h"
|
||||
|
||||
namespace al {
|
||||
|
||||
template<typename T, size_t alignment=alignof(T)>
|
||||
using vector = std::vector<T, al::allocator<T, alignment>>;
|
||||
|
||||
} // namespace al
|
||||
|
||||
#endif /* AL_VECTOR_H */
|
||||
117
externals/openal-soft/common/win_main_utf8.h
vendored
Normal file
117
externals/openal-soft/common/win_main_utf8.h
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef WIN_MAIN_UTF8_H
|
||||
#define WIN_MAIN_UTF8_H
|
||||
|
||||
/* For Windows systems this provides a way to get UTF-8 encoded argv strings,
|
||||
* and also overrides fopen to accept UTF-8 filenames. Working with wmain
|
||||
* directly complicates cross-platform compatibility, while normal main() in
|
||||
* Windows uses the current codepage (which has limited availability of
|
||||
* characters).
|
||||
*
|
||||
* For MinGW, you must link with -municode
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <memory>
|
||||
|
||||
#define STATIC_CAST(...) static_cast<__VA_ARGS__>
|
||||
#define REINTERPRET_CAST(...) reinterpret_cast<__VA_ARGS__>
|
||||
|
||||
#else
|
||||
|
||||
#define STATIC_CAST(...) (__VA_ARGS__)
|
||||
#define REINTERPRET_CAST(...) (__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
static FILE *my_fopen(const char *fname, const char *mode)
|
||||
{
|
||||
wchar_t *wname=NULL, *wmode=NULL;
|
||||
int namelen, modelen;
|
||||
FILE *file = NULL;
|
||||
errno_t err;
|
||||
|
||||
namelen = MultiByteToWideChar(CP_UTF8, 0, fname, -1, NULL, 0);
|
||||
modelen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
|
||||
|
||||
if(namelen <= 0 || modelen <= 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to convert UTF-8 fname \"%s\", mode \"%s\"\n", fname, mode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
auto strbuf = std::make_unique<wchar_t[]>(static_cast<size_t>(namelen)+modelen);
|
||||
wname = strbuf.get();
|
||||
#else
|
||||
wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)namelen + modelen);
|
||||
#endif
|
||||
wmode = wname + namelen;
|
||||
MultiByteToWideChar(CP_UTF8, 0, fname, -1, wname, namelen);
|
||||
MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen);
|
||||
|
||||
err = _wfopen_s(&file, wname, wmode);
|
||||
if(err)
|
||||
{
|
||||
errno = err;
|
||||
file = NULL;
|
||||
}
|
||||
|
||||
#ifndef __cplusplus
|
||||
free(wname);
|
||||
#endif
|
||||
return file;
|
||||
}
|
||||
#define fopen my_fopen
|
||||
|
||||
|
||||
/* SDL overrides main and provides UTF-8 args for us. */
|
||||
#if !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE)
|
||||
int my_main(int, char**);
|
||||
#define main my_main
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
int wmain(int argc, wchar_t **wargv)
|
||||
{
|
||||
char **argv;
|
||||
size_t total;
|
||||
int i;
|
||||
|
||||
total = sizeof(*argv) * STATIC_CAST(size_t)(argc);
|
||||
for(i = 0;i < argc;i++)
|
||||
total += STATIC_CAST(size_t)(WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL,
|
||||
NULL));
|
||||
|
||||
#ifdef __cplusplus
|
||||
auto argbuf = std::make_unique<char[]>(total);
|
||||
argv = reinterpret_cast<char**>(argbuf.get());
|
||||
#else
|
||||
argv = (char**)calloc(1, total);
|
||||
#endif
|
||||
argv[0] = REINTERPRET_CAST(char*)(argv + argc);
|
||||
for(i = 0;i < argc-1;i++)
|
||||
{
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL);
|
||||
argv[i+1] = argv[i] + len;
|
||||
}
|
||||
WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL);
|
||||
|
||||
#ifdef __cplusplus
|
||||
return main(argc, argv);
|
||||
#else
|
||||
i = main(argc, argv);
|
||||
|
||||
free(argv);
|
||||
return i;
|
||||
#endif
|
||||
}
|
||||
#endif /* !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE) */
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* WIN_MAIN_UTF8_H */
|
||||
Reference in New Issue
Block a user