mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-15 08:34:40 +00:00
Initial commit
added libraries: opus flac libsndfile updated: libvorbis libogg openal - Everything works as expected for now. Bare in mind libsndfile needed the check for whether or not it could find the xiph libraries removed in order for this to work.
This commit is contained in:
parent
05a083ca6f
commit
a745fc3757
1954 changed files with 431332 additions and 21037 deletions
|
|
@ -7,6 +7,7 @@
|
|||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "albit.h"
|
||||
|
|
@ -20,12 +21,6 @@ namespace {
|
|||
using ushort = unsigned short;
|
||||
using ushort2 = std::pair<ushort,ushort>;
|
||||
|
||||
/* Because std::array doesn't have constexpr non-const accessors in C++14. */
|
||||
template<typename T, size_t N>
|
||||
struct our_array {
|
||||
T mData[N];
|
||||
};
|
||||
|
||||
constexpr size_t BitReverseCounter(size_t log2_size) noexcept
|
||||
{
|
||||
/* Some magic math that calculates the number of swaps needed for a
|
||||
|
|
@ -34,51 +29,54 @@ constexpr size_t BitReverseCounter(size_t log2_size) noexcept
|
|||
return (1u<<(log2_size-1)) - (1u<<((log2_size-1u)/2u));
|
||||
}
|
||||
|
||||
|
||||
template<size_t N>
|
||||
constexpr auto GetBitReverser() noexcept
|
||||
{
|
||||
struct BitReverser {
|
||||
static_assert(N <= sizeof(ushort)*8, "Too many bits for the bit-reversal table.");
|
||||
|
||||
our_array<ushort2, BitReverseCounter(N)> ret{};
|
||||
const size_t fftsize{1u << N};
|
||||
size_t ret_i{0};
|
||||
ushort2 mData[BitReverseCounter(N)]{};
|
||||
|
||||
/* Bit-reversal permutation applied to a sequence of fftsize items. */
|
||||
for(size_t idx{1u};idx < fftsize-1;++idx)
|
||||
constexpr BitReverser()
|
||||
{
|
||||
size_t revidx{0u}, imask{idx};
|
||||
for(size_t i{0};i < N;++i)
|
||||
{
|
||||
revidx = (revidx<<1) | (imask&1);
|
||||
imask >>= 1;
|
||||
}
|
||||
const size_t fftsize{1u << N};
|
||||
size_t ret_i{0};
|
||||
|
||||
if(idx < revidx)
|
||||
/* Bit-reversal permutation applied to a sequence of fftsize items. */
|
||||
for(size_t idx{1u};idx < fftsize-1;++idx)
|
||||
{
|
||||
ret.mData[ret_i].first = static_cast<ushort>(idx);
|
||||
ret.mData[ret_i].second = static_cast<ushort>(revidx);
|
||||
++ret_i;
|
||||
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));
|
||||
}
|
||||
assert(ret_i == al::size(ret.mData));
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
/* These bit-reversal swap tables support up to 10-bit indices (1024 elements),
|
||||
* which is the largest used by OpenAL Soft's filters and effects. Larger FFT
|
||||
* requests, used by some utilities where performance is less important, will
|
||||
* use a slower table-less path.
|
||||
*/
|
||||
constexpr auto BitReverser2 = GetBitReverser<2>();
|
||||
constexpr auto BitReverser3 = GetBitReverser<3>();
|
||||
constexpr auto BitReverser4 = GetBitReverser<4>();
|
||||
constexpr auto BitReverser5 = GetBitReverser<5>();
|
||||
constexpr auto BitReverser6 = GetBitReverser<6>();
|
||||
constexpr auto BitReverser7 = GetBitReverser<7>();
|
||||
constexpr auto BitReverser8 = GetBitReverser<8>();
|
||||
constexpr auto BitReverser9 = GetBitReverser<9>();
|
||||
constexpr auto BitReverser10 = GetBitReverser<10>();
|
||||
constexpr al::span<const ushort2> gBitReverses[11]{
|
||||
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,
|
||||
|
|
@ -89,11 +87,13 @@ constexpr al::span<const ushort2> gBitReverses[11]{
|
|||
BitReverser8.mData,
|
||||
BitReverser9.mData,
|
||||
BitReverser10.mData
|
||||
};
|
||||
}};
|
||||
|
||||
} // namespace
|
||||
|
||||
void complex_fft(const al::span<std::complex<double>> buffer, const double sign)
|
||||
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
|
||||
|
|
@ -101,7 +101,7 @@ void complex_fft(const al::span<std::complex<double>> buffer, const double sign)
|
|||
*/
|
||||
const size_t log2_size{static_cast<size_t>(al::countr_zero(fftsize))};
|
||||
|
||||
if(unlikely(log2_size >= al::size(gBitReverses)))
|
||||
if(log2_size >= gBitReverses.size()) UNLIKELY
|
||||
{
|
||||
for(size_t idx{1u};idx < fftsize-1;++idx)
|
||||
{
|
||||
|
|
@ -120,21 +120,21 @@ void complex_fft(const al::span<std::complex<double>> buffer, const double sign)
|
|||
std::swap(buffer[rev.first], buffer[rev.second]);
|
||||
|
||||
/* Iterative form of Danielson-Lanczos lemma */
|
||||
const double pi{al::numbers::pi * sign};
|
||||
const Real pi{al::numbers::pi_v<Real> * sign};
|
||||
size_t step2{1u};
|
||||
for(size_t i{0};i < log2_size;++i)
|
||||
{
|
||||
const double arg{pi / static_cast<double>(step2)};
|
||||
const Real arg{pi / static_cast<Real>(step2)};
|
||||
|
||||
/* TODO: Would std::polar(1.0, arg) be any better? */
|
||||
const std::complex<double> w{std::cos(arg), std::sin(arg)};
|
||||
std::complex<double> u{1.0, 0.0};
|
||||
const 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<double> temp{buffer[k+step2] * u};
|
||||
std::complex<Real> temp{buffer[k+step2] * u};
|
||||
buffer[k+step2] = buffer[k] - temp;
|
||||
buffer[k] += temp;
|
||||
}
|
||||
|
|
@ -148,6 +148,8 @@ void complex_fft(const al::span<std::complex<double>> buffer, const double sign)
|
|||
|
||||
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());
|
||||
|
|
@ -156,11 +158,14 @@ void complex_hilbert(const al::span<std::complex<double>> buffer)
|
|||
|
||||
*bufiter *= inverse_size; ++bufiter;
|
||||
bufiter = std::transform(bufiter, halfiter, bufiter,
|
||||
[inverse_size](const std::complex<double> &c) -> std::complex<double>
|
||||
{ return c * (2.0*inverse_size); });
|
||||
[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);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define ALCOMPLEX_H
|
||||
|
||||
#include <complex>
|
||||
#include <type_traits>
|
||||
|
||||
#include "alspan.h"
|
||||
|
||||
|
|
@ -10,21 +11,27 @@
|
|||
* 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.
|
||||
*/
|
||||
void complex_fft(const al::span<std::complex<double>> buffer, const double sign);
|
||||
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.
|
||||
*/
|
||||
inline void forward_fft(const al::span<std::complex<double>> buffer)
|
||||
{ complex_fft(buffer, -1.0); }
|
||||
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.
|
||||
*/
|
||||
inline void inverse_fft(const al::span<std::complex<double>> buffer)
|
||||
{ complex_fft(buffer, 1.0); }
|
||||
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
|
||||
|
|
|
|||
|
|
@ -9,141 +9,17 @@
|
|||
|
||||
namespace al {
|
||||
|
||||
auto filebuf::underflow() -> int_type
|
||||
{
|
||||
if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
|
||||
{
|
||||
// Read in the next chunk of data, and set the pointers on success
|
||||
DWORD got{};
|
||||
if(ReadFile(mFile, mBuffer.data(), static_cast<DWORD>(mBuffer.size()), &got, nullptr))
|
||||
setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
|
||||
}
|
||||
if(gptr() == egptr())
|
||||
return traits_type::eof();
|
||||
return traits_type::to_int_type(*gptr());
|
||||
}
|
||||
|
||||
auto filebuf::seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) -> pos_type
|
||||
{
|
||||
if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
|
||||
return traits_type::eof();
|
||||
|
||||
LARGE_INTEGER fpos{};
|
||||
switch(whence)
|
||||
{
|
||||
case std::ios_base::beg:
|
||||
fpos.QuadPart = offset;
|
||||
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
|
||||
return traits_type::eof();
|
||||
break;
|
||||
|
||||
case std::ios_base::cur:
|
||||
// If the offset remains in the current buffer range, just
|
||||
// update the pointer.
|
||||
if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
|
||||
(offset < 0 && -offset <= off_type(gptr()-eback())))
|
||||
{
|
||||
// Get the current file offset to report the correct read
|
||||
// offset.
|
||||
fpos.QuadPart = 0;
|
||||
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
|
||||
return traits_type::eof();
|
||||
setg(eback(), gptr()+offset, egptr());
|
||||
return fpos.QuadPart - off_type(egptr()-gptr());
|
||||
}
|
||||
// Need to offset for the file offset being at egptr() while
|
||||
// the requested offset is relative to gptr().
|
||||
offset -= off_type(egptr()-gptr());
|
||||
fpos.QuadPart = offset;
|
||||
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
|
||||
return traits_type::eof();
|
||||
break;
|
||||
|
||||
case std::ios_base::end:
|
||||
fpos.QuadPart = offset;
|
||||
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
|
||||
return traits_type::eof();
|
||||
break;
|
||||
|
||||
default:
|
||||
return traits_type::eof();
|
||||
}
|
||||
setg(nullptr, nullptr, nullptr);
|
||||
return fpos.QuadPart;
|
||||
}
|
||||
|
||||
auto filebuf::seekpos(pos_type pos, std::ios_base::openmode mode) -> pos_type
|
||||
{
|
||||
// Simplified version of seekoff
|
||||
if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
|
||||
return traits_type::eof();
|
||||
|
||||
LARGE_INTEGER fpos{};
|
||||
fpos.QuadPart = pos;
|
||||
if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
|
||||
return traits_type::eof();
|
||||
|
||||
setg(nullptr, nullptr, nullptr);
|
||||
return fpos.QuadPart;
|
||||
}
|
||||
|
||||
filebuf::~filebuf()
|
||||
{ close(); }
|
||||
|
||||
bool filebuf::open(const wchar_t *filename, std::ios_base::openmode mode)
|
||||
{
|
||||
if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
|
||||
return false;
|
||||
HANDLE f{CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr)};
|
||||
if(f == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
if(mFile != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(mFile);
|
||||
mFile = f;
|
||||
|
||||
setg(nullptr, nullptr, nullptr);
|
||||
return true;
|
||||
}
|
||||
bool filebuf::open(const char *filename, std::ios_base::openmode mode)
|
||||
{
|
||||
std::wstring wname{utf8_to_wstr(filename)};
|
||||
return open(wname.c_str(), mode);
|
||||
}
|
||||
|
||||
void filebuf::close()
|
||||
{
|
||||
if(mFile != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(mFile);
|
||||
mFile = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
|
||||
ifstream::ifstream(const wchar_t *filename, std::ios_base::openmode mode)
|
||||
: std::istream{nullptr}
|
||||
{
|
||||
init(&mStreamBuf);
|
||||
|
||||
// Set the failbit if the file failed to open.
|
||||
if((mode&std::ios_base::out) || !mStreamBuf.open(filename, mode|std::ios_base::in))
|
||||
clear(failbit);
|
||||
}
|
||||
|
||||
ifstream::ifstream(const char *filename, std::ios_base::openmode mode)
|
||||
: std::istream{nullptr}
|
||||
{
|
||||
init(&mStreamBuf);
|
||||
: std::ifstream{utf8_to_wstr(filename).c_str(), mode}
|
||||
{ }
|
||||
|
||||
// Set the failbit if the file failed to open.
|
||||
if((mode&std::ios_base::out) || !mStreamBuf.open(filename, mode|std::ios_base::in))
|
||||
clear(failbit);
|
||||
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);
|
||||
}
|
||||
|
||||
/* This is only here to ensure the compiler doesn't define an implicit
|
||||
* destructor, which it tries to automatically inline and subsequently complain
|
||||
* it can't inline without excessive code growth.
|
||||
*/
|
||||
ifstream::~ifstream() { }
|
||||
ifstream::~ifstream() = default;
|
||||
|
||||
} // namespace al
|
||||
|
||||
|
|
|
|||
|
|
@ -3,57 +3,29 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
namespace al {
|
||||
|
||||
// Windows' std::ifstream fails with non-ANSI paths since the standard only
|
||||
// specifies names using const char* (or std::string). MSVC has a non-standard
|
||||
// extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
|
||||
// but not all Windows compilers support it. So we have to make our own istream
|
||||
// that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
|
||||
class filebuf final : public std::streambuf {
|
||||
std::array<char_type,4096> mBuffer;
|
||||
HANDLE mFile{INVALID_HANDLE_VALUE};
|
||||
|
||||
int_type underflow() override;
|
||||
pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override;
|
||||
pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override;
|
||||
|
||||
// Inherit from std::ifstream to accept UTF-8 filenames
|
||||
class ifstream final : public std::ifstream {
|
||||
public:
|
||||
filebuf() = default;
|
||||
~filebuf() override;
|
||||
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} { }
|
||||
|
||||
bool open(const wchar_t *filename, std::ios_base::openmode mode);
|
||||
bool open(const char *filename, std::ios_base::openmode 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} { }
|
||||
|
||||
bool is_open() const noexcept { return mFile != INVALID_HANDLE_VALUE; }
|
||||
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); }
|
||||
|
||||
void close();
|
||||
};
|
||||
|
||||
// Inherit from std::istream to use our custom streambuf
|
||||
class ifstream final : public std::istream {
|
||||
filebuf mStreamBuf;
|
||||
|
||||
public:
|
||||
ifstream(const wchar_t *filename, std::ios_base::openmode mode = std::ios_base::in);
|
||||
ifstream(const std::wstring &filename, std::ios_base::openmode mode = std::ios_base::in)
|
||||
: ifstream(filename.c_str(), mode) { }
|
||||
ifstream(const char *filename, std::ios_base::openmode mode = std::ios_base::in);
|
||||
ifstream(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in)
|
||||
: ifstream(filename.c_str(), mode) { }
|
||||
~ifstream() override;
|
||||
|
||||
bool is_open() const noexcept { return mStreamBuf.is_open(); }
|
||||
|
||||
void close() { mStreamBuf.close(); }
|
||||
};
|
||||
|
||||
} // namespace al
|
||||
|
|
@ -64,7 +36,6 @@ public:
|
|||
|
||||
namespace al {
|
||||
|
||||
using filebuf = std::filebuf;
|
||||
using ifstream = std::ifstream;
|
||||
|
||||
} // namespace al
|
||||
|
|
|
|||
|
|
@ -29,9 +29,11 @@ void *al_calloc(size_t alignment, size_t size);
|
|||
#define DEF_NEWDEL(T) \
|
||||
void *operator new(size_t size) \
|
||||
{ \
|
||||
void *ret = al_malloc(alignof(T), size); \
|
||||
if(!ret) throw std::bad_alloc(); \
|
||||
return ret; \
|
||||
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); } \
|
||||
|
|
@ -50,6 +52,8 @@ 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)); \
|
||||
} \
|
||||
|
|
@ -68,8 +72,10 @@ enum FamCount : size_t { };
|
|||
|
||||
namespace al {
|
||||
|
||||
template<typename T, std::size_t alignment=alignof(T)>
|
||||
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&;
|
||||
|
|
@ -81,7 +87,7 @@ struct allocator {
|
|||
|
||||
template<typename U>
|
||||
struct rebind {
|
||||
using other = allocator<U, (alignment<alignof(U))?alignof(U):alignment>;
|
||||
using other = allocator<U, Align>;
|
||||
};
|
||||
|
||||
constexpr explicit allocator() noexcept = default;
|
||||
|
|
@ -102,6 +108,18 @@ 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)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include "altraits.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
|
|
@ -97,12 +98,20 @@ inline uint32_t NextPowerOf2(uint32_t value) noexcept
|
|||
return value+1;
|
||||
}
|
||||
|
||||
/** Round up a value to the next multiple. */
|
||||
inline size_t RoundUp(size_t value, size_t r) noexcept
|
||||
{
|
||||
value += r-1;
|
||||
return value - (value%r);
|
||||
}
|
||||
/**
|
||||
* 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); }
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -161,11 +170,11 @@ inline int float2int(float f) noexcept
|
|||
shift = ((conv.i>>23)&0xff) - (127+23);
|
||||
|
||||
/* Over/underflow */
|
||||
if UNLIKELY(shift >= 31 || shift < -23)
|
||||
if(shift >= 31 || shift < -23) UNLIKELY
|
||||
return 0;
|
||||
|
||||
mant = (conv.i&0x7fffff) | 0x800000;
|
||||
if LIKELY(shift < 0)
|
||||
if(shift < 0) LIKELY
|
||||
return (mant >> -shift) * sign;
|
||||
return (mant << shift) * sign;
|
||||
|
||||
|
|
@ -198,11 +207,11 @@ inline int double2int(double d) noexcept
|
|||
shift = ((conv.i64 >> 52) & 0x7ff) - (1023 + 52);
|
||||
|
||||
/* Over/underflow */
|
||||
if UNLIKELY(shift >= 63 || shift < -52)
|
||||
if(shift >= 63 || shift < -52) UNLIKELY
|
||||
return 0;
|
||||
|
||||
mant = (conv.i64 & 0xfffffffffffff_i64) | 0x10000000000000_i64;
|
||||
if LIKELY(shift < 0)
|
||||
if(shift < 0) LIKELY
|
||||
return (int)(mant >> -shift) * sign;
|
||||
return (int)(mant << shift) * sign;
|
||||
|
||||
|
|
@ -251,7 +260,7 @@ inline float fast_roundf(float f) noexcept
|
|||
sign = (conv.i>>31)&0x01;
|
||||
expo = (conv.i>>23)&0xff;
|
||||
|
||||
if UNLIKELY(expo >= 150/*+23*/)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -310,10 +310,10 @@ public:
|
|||
|
||||
template<typename U>
|
||||
constexpr T value_or(U&& defval) const&
|
||||
{ return bool{*this} ? **this : static_cast<T>(std::forward<U>(defval)); }
|
||||
{ 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)); }
|
||||
{ return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr T& emplace(Args&& ...args)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "altraits.h"
|
||||
|
||||
namespace al {
|
||||
|
||||
template<typename T>
|
||||
|
|
@ -42,37 +45,39 @@ class span;
|
|||
|
||||
namespace detail_ {
|
||||
template<typename... Ts>
|
||||
struct make_void { using type = void; };
|
||||
template<typename... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
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>
|
||||
using is_span = is_span_<std::remove_cv_t<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>
|
||||
using is_std_array = is_std_array_<std::remove_cv_t<T>>;
|
||||
constexpr bool is_std_array_v = is_std_array_<std::remove_cv_t<T>>::value;
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct has_size_and_data : std::false_type { };
|
||||
constexpr bool has_size_and_data = false;
|
||||
template<typename T>
|
||||
struct has_size_and_data<T,
|
||||
constexpr bool has_size_and_data<T,
|
||||
void_t<decltype(al::size(std::declval<T>())), decltype(al::data(std::declval<T>()))>>
|
||||
: std::true_type { };
|
||||
= 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(...) bool rt_=true, std::enable_if_t<rt_ && (__VA_ARGS__),bool> = true
|
||||
#define IS_VALID_CONTAINER(C) \
|
||||
!detail_::is_span<C>::value && !detail_::is_std_array<C>::value && \
|
||||
!std::is_array<C>::value && detail_::has_size_and_data<C>::value && \
|
||||
std::is_convertible<std::remove_pointer_t<decltype(al::data(std::declval<C&>()))>(*)[],element_type(*)[]>::value
|
||||
#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
|
||||
|
||||
template<typename T, size_t E>
|
||||
class span {
|
||||
|
|
@ -94,23 +99,33 @@ public:
|
|||
|
||||
static constexpr size_t extent{E};
|
||||
|
||||
template<REQUIRES(extent==0)>
|
||||
template<bool is0=(extent == 0), REQUIRES(is0)>
|
||||
constexpr span() noexcept { }
|
||||
constexpr span(pointer ptr, index_type /*count*/) : mData{ptr} { }
|
||||
constexpr span(pointer first, pointer /*last*/) : mData{first} { }
|
||||
constexpr span(element_type (&arr)[E]) noexcept : span{al::data(arr), al::size(arr)} { }
|
||||
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<REQUIRES(std::is_const<element_type>::value)>
|
||||
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(IS_VALID_CONTAINER(U))>
|
||||
constexpr span(U &cont) : span{al::data(cont), al::size(cont)} { }
|
||||
template<typename U, REQUIRES(IS_VALID_CONTAINER(const U))>
|
||||
constexpr span(const U &cont) : span{al::data(cont), al::size(cont)} { }
|
||||
template<typename U, REQUIRES(!std::is_same<element_type,U>::value
|
||||
&& std::is_convertible<U(*)[],element_type(*)[]>::value)>
|
||||
constexpr span(const span<U,E> &span_) noexcept : span{al::data(span_), al::size(span_)} { }
|
||||
|
||||
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;
|
||||
|
|
@ -199,22 +214,30 @@ public:
|
|||
static constexpr size_t extent{dynamic_extent};
|
||||
|
||||
constexpr span() noexcept = default;
|
||||
constexpr span(pointer ptr, index_type count) : mData{ptr}, mDataEnd{ptr+count} { }
|
||||
constexpr span(pointer first, pointer last) : mData{first}, mDataEnd{last} { }
|
||||
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(element_type (&arr)[N]) noexcept : span{al::data(arr), al::size(arr)} { }
|
||||
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, REQUIRES(std::is_const<element_type>::value)>
|
||||
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(IS_VALID_CONTAINER(U))>
|
||||
constexpr span(U &cont) : span{al::data(cont), al::size(cont)} { }
|
||||
template<typename U, REQUIRES(IS_VALID_CONTAINER(const U))>
|
||||
constexpr span(const U &cont) : span{al::data(cont), al::size(cont)} { }
|
||||
|
||||
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)
|
||||
&& std::is_convertible<U(*)[],element_type(*)[]>::value)>
|
||||
&& 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;
|
||||
|
||||
|
|
@ -299,7 +322,31 @@ constexpr inline auto span<T,E>::subspan(size_t offset, size_t count) const
|
|||
span<element_type>{mData+offset, mData+offset+count};
|
||||
}
|
||||
|
||||
#undef IS_VALID_CONTAINER
|
||||
/* 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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define AL_STRING_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace al {
|
||||
|
|
|
|||
14
Engine/lib/openal-soft/common/altraits.h
Normal file
14
Engine/lib/openal-soft/common/altraits.h
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 */
|
||||
|
|
@ -44,7 +44,7 @@ public:
|
|||
}
|
||||
ComPtr& operator=(ComPtr&& rhs)
|
||||
{
|
||||
if(likely(&rhs != this))
|
||||
if(&rhs != this) LIKELY
|
||||
{
|
||||
if(mPtr) mPtr->Release();
|
||||
mPtr = std::exchange(rhs.mPtr, nullptr);
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ class intrusive_ref {
|
|||
|
||||
public:
|
||||
unsigned int add_ref() noexcept { return IncrementRef(mRef); }
|
||||
unsigned int release() noexcept
|
||||
unsigned int dec_ref() noexcept
|
||||
{
|
||||
auto ref = DecrementRef(mRef);
|
||||
if UNLIKELY(ref == 0)
|
||||
if(ref == 0) UNLIKELY
|
||||
delete static_cast<T*>(this);
|
||||
return ref;
|
||||
}
|
||||
|
|
@ -58,22 +58,22 @@ public:
|
|||
{ rhs.mPtr = nullptr; }
|
||||
intrusive_ptr(std::nullptr_t) noexcept { }
|
||||
explicit intrusive_ptr(T *ptr) noexcept : mPtr{ptr} { }
|
||||
~intrusive_ptr() { if(mPtr) mPtr->release(); }
|
||||
~intrusive_ptr() { if(mPtr) mPtr->dec_ref(); }
|
||||
|
||||
intrusive_ptr& operator=(const intrusive_ptr &rhs) noexcept
|
||||
{
|
||||
static_assert(noexcept(std::declval<T*>()->release()), "release must be noexcept");
|
||||
static_assert(noexcept(std::declval<T*>()->dec_ref()), "dec_ref must be noexcept");
|
||||
|
||||
if(rhs.mPtr) rhs.mPtr->add_ref();
|
||||
if(mPtr) mPtr->release();
|
||||
if(mPtr) mPtr->dec_ref();
|
||||
mPtr = rhs.mPtr;
|
||||
return *this;
|
||||
}
|
||||
intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept
|
||||
{
|
||||
if(likely(&rhs != this))
|
||||
if(&rhs != this) LIKELY
|
||||
{
|
||||
if(mPtr) mPtr->release();
|
||||
if(mPtr) mPtr->dec_ref();
|
||||
mPtr = std::exchange(rhs.mPtr, nullptr);
|
||||
}
|
||||
return *this;
|
||||
|
|
@ -88,7 +88,7 @@ public:
|
|||
void reset(T *ptr=nullptr) noexcept
|
||||
{
|
||||
if(mPtr)
|
||||
mPtr->release();
|
||||
mPtr->dec_ref();
|
||||
mPtr = ptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef __has_builtin
|
||||
#define HAS_BUILTIN __has_builtin
|
||||
|
|
@ -11,63 +11,69 @@
|
|||
#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]]
|
||||
#define force_inline [[gnu::always_inline]] inline
|
||||
#elif defined(_MSC_VER)
|
||||
#define force_inline __forceinline
|
||||
#else
|
||||
#define force_inline inline
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || HAS_BUILTIN(__builtin_expect)
|
||||
/* likely() optimizes for the case where the condition is true. The condition
|
||||
* is not required to be true, but it can result in more optimal code for the
|
||||
* true path at the expense of a less optimal false path.
|
||||
/* 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.
|
||||
*/
|
||||
template<typename T>
|
||||
force_inline constexpr bool likely(T&& expr) noexcept
|
||||
{ return __builtin_expect(static_cast<bool>(std::forward<T>(expr)), true); }
|
||||
/* The opposite of likely(), optimizing for the case where the condition is
|
||||
* false.
|
||||
*/
|
||||
template<typename T>
|
||||
force_inline constexpr bool unlikely(T&& expr) noexcept
|
||||
{ return __builtin_expect(static_cast<bool>(std::forward<T>(expr)), false); }
|
||||
|
||||
#else
|
||||
|
||||
template<typename T>
|
||||
force_inline constexpr bool likely(T&& expr) noexcept
|
||||
{ return static_cast<bool>(std::forward<T>(expr)); }
|
||||
template<typename T>
|
||||
force_inline constexpr bool unlikely(T&& expr) noexcept
|
||||
{ return static_cast<bool>(std::forward<T>(expr)); }
|
||||
#endif
|
||||
#define LIKELY(x) (likely(x))
|
||||
#define UNLIKELY(x) (unlikely(x))
|
||||
|
||||
#if HAS_BUILTIN(__builtin_assume)
|
||||
/* Unlike LIKELY, ASSUME requires the condition to be true or else it invokes
|
||||
* undefined behavior. It's essentially an assert without actually checking the
|
||||
* condition at run-time, allowing for stronger optimizations than LIKELY.
|
||||
*/
|
||||
#define ASSUME __builtin_assume
|
||||
#elif defined(_MSC_VER)
|
||||
#define ASSUME __assume
|
||||
#elif defined(__GNUC__)
|
||||
#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 defined(__clang__) || (defined(__GNUC__) && !defined(__ICC))
|
||||
#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};
|
||||
|
|
|
|||
|
|
@ -53,12 +53,12 @@ struct PhaseShifterT {
|
|||
std::fill_n(fftBuffer.get(), fft_size, complex_d{});
|
||||
fftBuffer[half_size] = 1.0;
|
||||
|
||||
forward_fft({fftBuffer.get(), fft_size});
|
||||
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({fftBuffer.get(), fft_size});
|
||||
inverse_fft(al::as_span(fftBuffer.get(), fft_size));
|
||||
|
||||
auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1);
|
||||
for(float &coeff : mCoeffs)
|
||||
|
|
@ -69,7 +69,6 @@ struct PhaseShifterT {
|
|||
}
|
||||
|
||||
void process(al::span<float> dst, const float *RESTRICT src) const;
|
||||
void processAccum(al::span<float> dst, const float *RESTRICT src) const;
|
||||
|
||||
private:
|
||||
#if defined(HAVE_NEON)
|
||||
|
|
@ -212,103 +211,4 @@ inline void PhaseShifterT<S>::process(al::span<float> dst, const float *RESTRICT
|
|||
#endif
|
||||
}
|
||||
|
||||
template<size_t S>
|
||||
inline void PhaseShifterT<S>::processAccum(al::span<float> dst, const float *RESTRICT src) const
|
||||
{
|
||||
#ifdef HAVE_SSE_INTRINSICS
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
auto *out = reinterpret_cast<__m64*>(dst.data());
|
||||
do {
|
||||
__m128 r04{_mm_setzero_ps()};
|
||||
__m128 r14{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
const __m128 s0{_mm_loadu_ps(&src[j*2])};
|
||||
const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])};
|
||||
|
||||
__m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))};
|
||||
r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs));
|
||||
|
||||
s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1));
|
||||
r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
src += 2;
|
||||
|
||||
__m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))};
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
_mm_storel_pi(out, _mm_add_ps(_mm_loadl_pi(_mm_undefined_ps(), out), r4));
|
||||
++out;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
__m128 r4{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
dst.back() += _mm_cvtss_f32(r4);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_NEON)
|
||||
|
||||
size_t pos{0};
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
do {
|
||||
float32x4_t r04{vdupq_n_f32(0.0f)};
|
||||
float32x4_t r14{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s0{vld1q_f32(&src[j*2])};
|
||||
const float32x4_t s1{vld1q_f32(&src[j*2 + 4])};
|
||||
|
||||
r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs);
|
||||
r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs);
|
||||
}
|
||||
src += 2;
|
||||
|
||||
float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))};
|
||||
float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))};
|
||||
|
||||
vst1_f32(&dst[pos], vadd_f32(vld1_f32(&dst[pos]), r2));
|
||||
pos += 2;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
float32x4_t r4{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = vmlaq_f32(r4, s, coeffs);
|
||||
}
|
||||
r4 = vaddq_f32(r4, vrev64q_f32(r4));
|
||||
dst[pos] += vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for(float &output : dst)
|
||||
{
|
||||
float ret{0.0f};
|
||||
for(size_t j{0};j < mCoeffs.size();++j)
|
||||
ret += src[j*2] * mCoeffs[j];
|
||||
|
||||
output += ret;
|
||||
++src;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* PHASE_SHIFTER_H */
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ using uint = unsigned int;
|
|||
*/
|
||||
double Sinc(const double x)
|
||||
{
|
||||
if(unlikely(std::abs(x) < Epsilon))
|
||||
if(std::abs(x) < Epsilon) UNLIKELY
|
||||
return 1.0;
|
||||
return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
|
||||
}
|
||||
|
|
@ -96,7 +96,7 @@ constexpr uint Gcd(uint x, uint y)
|
|||
constexpr uint CalcKaiserOrder(const double rejection, const double transition)
|
||||
{
|
||||
const double w_t{2.0 * al::numbers::pi * transition};
|
||||
if LIKELY(rejection > 21.0)
|
||||
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));
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ constexpr uint CalcKaiserOrder(const double rejection, const double transition)
|
|||
// Calculates the beta value of the Kaiser window. Rejection is in dB.
|
||||
constexpr double CalcKaiserBeta(const double rejection)
|
||||
{
|
||||
if LIKELY(rejection > 50.0)
|
||||
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)) +
|
||||
|
|
@ -171,13 +171,13 @@ void PPhaseResampler::init(const uint srcRate, const uint dstRate)
|
|||
// polyphase filter implementation.
|
||||
void PPhaseResampler::process(const uint inN, const double *in, const uint outN, double *out)
|
||||
{
|
||||
if UNLIKELY(outN == 0)
|
||||
if(outN == 0) UNLIKELY
|
||||
return;
|
||||
|
||||
// Handle in-place operation.
|
||||
std::vector<double> workspace;
|
||||
double *work{out};
|
||||
if UNLIKELY(work == in)
|
||||
if(work == in) UNLIKELY
|
||||
{
|
||||
workspace.resize(outN);
|
||||
work = workspace.data();
|
||||
|
|
@ -195,17 +195,17 @@ void PPhaseResampler::process(const uint inN, const double *in, const uint outN,
|
|||
|
||||
// Only take input when 0 <= j_s < inN.
|
||||
double r{0.0};
|
||||
if LIKELY(j_f < m)
|
||||
if(j_f < m) LIKELY
|
||||
{
|
||||
size_t filt_len{(m-j_f+p-1) / p};
|
||||
if LIKELY(j_s+1 > inN)
|
||||
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)})
|
||||
if(size_t todo{std::min<size_t>(j_s+1, filt_len)}) LIKELY
|
||||
{
|
||||
do {
|
||||
r += f[j_f] * in[j_s];
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#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.
|
||||
*
|
||||
|
|
@ -32,11 +34,11 @@
|
|||
*/
|
||||
|
||||
struct PPhaseResampler {
|
||||
using uint = unsigned int;
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ al::optional<std::string> getenv(const char *envname)
|
|||
{
|
||||
const char *str{std::getenv(envname)};
|
||||
if(str && str[0] != '\0')
|
||||
return al::make_optional<std::string>(str);
|
||||
return str;
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ al::optional<std::wstring> getenv(const WCHAR *envname)
|
|||
{
|
||||
const WCHAR *str{_wgetenv(envname)};
|
||||
if(str && str[0] != L'\0')
|
||||
return al::make_optional<std::wstring>(str);
|
||||
return str;
|
||||
return al::nullopt;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@
|
|||
|
||||
void althrd_setname(const char *name)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_MSC_VER) && !defined(_M_ARM)
|
||||
|
||||
#define MS_VC_EXCEPTION 0x406D1388
|
||||
#pragma pack(push,8)
|
||||
struct {
|
||||
|
|
@ -55,7 +56,9 @@ void althrd_setname(const char *name)
|
|||
__except(EXCEPTION_CONTINUE_EXECUTION) {
|
||||
}
|
||||
#undef MS_VC_EXCEPTION
|
||||
|
||||
#else
|
||||
|
||||
(void)name;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -76,7 +79,7 @@ semaphore::~semaphore()
|
|||
|
||||
void semaphore::post()
|
||||
{
|
||||
if UNLIKELY(!ReleaseSemaphore(static_cast<HANDLE>(mSem), 1, nullptr))
|
||||
if(!ReleaseSemaphore(static_cast<HANDLE>(mSem), 1, nullptr))
|
||||
throw std::system_error(std::make_error_code(std::errc::value_too_large));
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +103,8 @@ namespace {
|
|||
|
||||
using setname_t1 = int(*)(const char*);
|
||||
using setname_t2 = int(*)(pthread_t, const char*);
|
||||
using setname_t3 = int(*)(pthread_t, const char*, void*);
|
||||
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); }
|
||||
|
|
@ -109,6 +113,9 @@ 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
|
||||
|
|
@ -125,6 +132,7 @@ void althrd_setname(const char *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__
|
||||
|
|
|
|||
|
|
@ -14,19 +14,19 @@ namespace alu {
|
|||
template<typename T>
|
||||
class VectorR {
|
||||
static_assert(std::is_floating_point<T>::value, "Must use floating-point types");
|
||||
alignas(16) std::array<T,4> mVals;
|
||||
alignas(16) T mVals[4];
|
||||
|
||||
public:
|
||||
constexpr VectorR() noexcept = default;
|
||||
constexpr VectorR(const VectorR&) noexcept = default;
|
||||
constexpr VectorR(T a, T b, T c, T d) noexcept : mVals{{a, b, c, d}} { }
|
||||
constexpr explicit VectorR(T a, T b, T c, T d) noexcept : mVals{a, b, c, d} { }
|
||||
|
||||
constexpr VectorR& operator=(const VectorR&) noexcept = default;
|
||||
|
||||
T& operator[](size_t idx) noexcept { return mVals[idx]; }
|
||||
constexpr T& operator[](size_t idx) noexcept { return mVals[idx]; }
|
||||
constexpr const T& operator[](size_t idx) const noexcept { return mVals[idx]; }
|
||||
|
||||
VectorR& operator+=(const VectorR &rhs) noexcept
|
||||
constexpr VectorR& operator+=(const VectorR &rhs) noexcept
|
||||
{
|
||||
mVals[0] += rhs.mVals[0];
|
||||
mVals[1] += rhs.mVals[1];
|
||||
|
|
@ -35,14 +35,13 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
VectorR operator-(const VectorR &rhs) const noexcept
|
||||
constexpr VectorR operator-(const VectorR &rhs) const noexcept
|
||||
{
|
||||
const VectorR ret{mVals[0] - rhs.mVals[0], mVals[1] - rhs.mVals[1],
|
||||
return VectorR{mVals[0] - rhs.mVals[0], mVals[1] - rhs.mVals[1],
|
||||
mVals[2] - rhs.mVals[2], mVals[3] - rhs.mVals[3]};
|
||||
return ret;
|
||||
}
|
||||
|
||||
T normalize(T limit = std::numeric_limits<T>::epsilon())
|
||||
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]};
|
||||
|
|
@ -59,38 +58,39 @@ public:
|
|||
return T{0};
|
||||
}
|
||||
|
||||
constexpr VectorR cross_product(const alu::VectorR<T> &rhs) const
|
||||
constexpr VectorR cross_product(const alu::VectorR<T> &rhs) const noexcept
|
||||
{
|
||||
return VectorR{
|
||||
(*this)[1]*rhs[2] - (*this)[2]*rhs[1],
|
||||
(*this)[2]*rhs[0] - (*this)[0]*rhs[2],
|
||||
(*this)[0]*rhs[1] - (*this)[1]*rhs[0],
|
||||
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
|
||||
{ return (*this)[0]*rhs[0] + (*this)[1]*rhs[1] + (*this)[2]*rhs[2]; }
|
||||
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) std::array<T,16> mVals;
|
||||
alignas(16) T mVals[16];
|
||||
|
||||
public:
|
||||
constexpr MatrixR() noexcept = default;
|
||||
constexpr MatrixR(const MatrixR&) noexcept = default;
|
||||
constexpr 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 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;
|
||||
|
||||
auto operator[](size_t idx) noexcept { return al::span<T,4>{&mVals[idx*4], 4}; }
|
||||
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}; }
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ public:
|
|||
using Matrix = MatrixR<float>;
|
||||
|
||||
template<typename T>
|
||||
inline VectorR<T> operator*(const MatrixR<T> &mtx, const VectorR<T> &vec) noexcept
|
||||
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],
|
||||
|
|
@ -115,13 +115,6 @@ inline VectorR<T> operator*(const MatrixR<T> &mtx, const VectorR<T> &vec) noexce
|
|||
vec[0]*mtx[0][3] + vec[1]*mtx[1][3] + vec[2]*mtx[2][3] + vec[3]*mtx[3][3]};
|
||||
}
|
||||
|
||||
template<typename U, typename T>
|
||||
inline VectorR<U> cast_to(const VectorR<T> &vec) noexcept
|
||||
{
|
||||
return VectorR<U>{static_cast<U>(vec[0]), static_cast<U>(vec[1]),
|
||||
static_cast<U>(vec[2]), static_cast<U>(vec[3])};
|
||||
}
|
||||
|
||||
} // namespace alu
|
||||
|
||||
#endif /* COMMON_VECMAT_H */
|
||||
|
|
|
|||
|
|
@ -44,10 +44,10 @@ static FILE *my_fopen(const char *fname, const char *mode)
|
|||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
auto strbuf = std::make_unique<wchar_t[]>(static_cast<size_t>(namelen+modelen));
|
||||
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));
|
||||
wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)namelen + modelen);
|
||||
#endif
|
||||
wmode = wname + namelen;
|
||||
MultiByteToWideChar(CP_UTF8, 0, fname, -1, wname, namelen);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue