update openal-soft to 1.24.3

keeping the alt 87514151c4 (diff-73a8dc1ce58605f6c5ea53548454c3bae516ec5132a29c9d7ff7edf9730c75be)
This commit is contained in:
AzaezelX 2025-09-03 11:09:27 -05:00
parent 12db0500e8
commit ba32094b7b
276 changed files with 49304 additions and 8712 deletions

View file

@ -8,7 +8,6 @@
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <sstream>
@ -16,7 +15,8 @@
#include "albit.h"
#include "alspan.h"
#include "opthelpers.h"
#include "filesystem.h"
#include "fmt/core.h"
namespace {
@ -43,35 +43,13 @@ enum class ReaderScope {
HFMatrix,
};
#ifdef __MINGW32__
[[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]]
#else
[[gnu::format(printf,2,3)]]
#endif
std::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
template<typename ...Args>
auto make_error(size_t linenum, fmt::format_string<Args...> fmt, Args&& ...args)
-> std::optional<std::string>
{
std::optional<std::string> ret;
auto &str = ret.emplace();
str.resize(256);
int printed{std::snprintf(str.data(), str.length(), "Line %zu: ", linenum)};
if(printed < 0) printed = 0;
auto plen = std::min(static_cast<size_t>(printed), str.length());
/* NOLINTBEGIN(*-array-to-pointer-decay) */
std::va_list args, args2;
va_start(args, fmt);
va_copy(args2, args);
const int msglen{std::vsnprintf(&str[plen], str.size()-plen, fmt, args)};
if(msglen >= 0 && static_cast<size_t>(msglen) >= str.size()-plen)
{
str.resize(static_cast<size_t>(msglen) + plen + 1u);
std::vsnprintf(&str[plen], str.size()-plen, fmt, args2);
}
va_end(args2);
va_end(args);
/* NOLINTEND(*-array-to-pointer-decay) */
auto &str = ret.emplace(fmt::format("Line {}: ", linenum));
str += fmt::format(std::move(fmt), std::forward<Args>(args)...);
return ret;
}
@ -82,7 +60,7 @@ AmbDecConf::~AmbDecConf() = default;
std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
{
std::ifstream f{std::filesystem::u8path(fname)};
fs::ifstream f{fs::u8path(fname)};
if(!f.is_open())
return std::string("Failed to open file \"")+fname+"\"";
@ -105,7 +83,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
if(command == "/}")
{
if(scope == ReaderScope::Global)
return make_error(linenum, "Unexpected /} in global scope");
return make_error(linenum, "Unexpected /}} in global scope");
scope = ReaderScope::Global;
continue;
}
@ -125,7 +103,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
istr >> spkr.Connection;
}
else
return make_error(linenum, "Unexpected speakers command: %s", command.c_str());
return make_error(linenum, "Unexpected speakers command: {}", command);
}
else if(scope == ReaderScope::LFMatrix || scope == ReaderScope::HFMatrix)
{
@ -168,7 +146,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
}
}
else
return make_error(linenum, "Unexpected matrix command: %s", command.c_str());
return make_error(linenum, "Unexpected matrix command: {}", command);
}
// Global scope commands
else if(command == "/description")
@ -185,7 +163,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
return make_error(linenum, "Duplicate version definition");
istr >> Version;
if(Version != 3)
return make_error(linenum, "Unsupported version: %d", Version);
return make_error(linenum, "Unsupported version: {}", Version);
}
else if(command == "/dec/chan_mask")
{
@ -194,7 +172,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
istr >> std::hex >> ChanMask >> std::dec;
if(!ChanMask || ChanMask > Ambi4OrderMask)
return make_error(linenum, "Invalid chan_mask: 0x%x", ChanMask);
return make_error(linenum, "Invalid chan_mask: {:#x}", ChanMask);
if(ChanMask > Ambi3OrderMask && CoeffScale == AmbDecScale::FuMa)
return make_error(linenum, "FuMa not compatible with over third-order");
}
@ -204,7 +182,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
return make_error(linenum, "Duplicate freq_bands");
istr >> FreqBands;
if(FreqBands != 1 && FreqBands != 2)
return make_error(linenum, "Invalid freq_bands: %u", FreqBands);
return make_error(linenum, "Invalid freq_bands: {}", FreqBands);
}
else if(command == "/dec/speakers")
{
@ -213,7 +191,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
size_t numspeakers{};
istr >> numspeakers;
if(!numspeakers)
return make_error(linenum, "Invalid speakers: %zu", numspeakers);
return make_error(linenum, "Invalid speakers: {}", numspeakers);
Speakers.resize(numspeakers);
}
else if(command == "/dec/coeff_scale")
@ -226,7 +204,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D;
else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa;
else
return make_error(linenum, "Unexpected coeff_scale: %s", scale.c_str());
return make_error(linenum, "Unexpected coeff_scale: {}", scale);
if(ChanMask > Ambi3OrderMask && CoeffScale == AmbDecScale::FuMa)
return make_error(linenum, "FuMa not compatible with over third-order");
@ -268,8 +246,8 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
if(FreqBands == 1)
{
if(command != "/matrix/{")
return make_error(linenum, "Unexpected \"%s\" for a single-band decoder",
command.c_str());
return make_error(linenum, "Unexpected \"{}\" for a single-band decoder",
command);
scope = ReaderScope::HFMatrix;
}
else
@ -279,15 +257,16 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
else if(command == "/hfmatrix/{")
scope = ReaderScope::HFMatrix;
else
return make_error(linenum, "Unexpected \"%s\" for a dual-band decoder",
command.c_str());
return make_error(linenum, "Unexpected \"{}\" for a dual-band decoder",
command);
}
}
else if(command == "/end")
{
const auto endpos = static_cast<std::size_t>(istr.tellg());
if(!is_at_end(buffer, endpos))
return make_error(linenum, "Extra junk on end: %s", buffer.substr(endpos).c_str());
return make_error(linenum, "Extra junk on end: {}",
std::string_view{buffer}.substr(endpos));
if(speaker_pos < Speakers.size() || hfmatrix_pos < Speakers.size()
|| (FreqBands == 2 && lfmatrix_pos < Speakers.size()))
@ -298,12 +277,13 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
return std::nullopt;
}
else
return make_error(linenum, "Unexpected command: %s", command.c_str());
return make_error(linenum, "Unexpected command: {}", command);
istr.clear();
const auto endpos = static_cast<std::size_t>(istr.tellg());
if(!is_at_end(buffer, endpos))
return make_error(linenum, "Extra junk on line: %s", buffer.substr(endpos).c_str());
return make_error(linenum, "Extra junk on line: {}",
std::string_view{buffer}.substr(endpos));
buffer.clear();
}
return make_error(linenum, "Unexpected end of file");

View file

@ -10,7 +10,6 @@ namespace {
using AmbiChannelFloatArray = std::array<float,MaxAmbiChannels>;
constexpr auto inv_sqrt2f = static_cast<float>(1.0/al::numbers::sqrt2);
constexpr auto inv_sqrt3f = static_cast<float>(1.0/al::numbers::sqrt3);
@ -76,16 +75,20 @@ static_assert(FirstOrderDecoder.size() == FirstOrderEncoder.size(), "First-order
* content.
*/
constexpr std::array FirstOrder2DDecoder{
std::array{2.500000000e-01f, 2.041241452e-01f, 0.0f, 2.041241452e-01f},
std::array{2.500000000e-01f, 2.041241452e-01f, 0.0f, -2.041241452e-01f},
std::array{2.500000000e-01f, -2.041241452e-01f, 0.0f, 2.041241452e-01f},
std::array{2.500000000e-01f, -2.041241452e-01f, 0.0f, -2.041241452e-01f},
std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f},
std::array{1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f},
std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f},
std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f},
std::array{1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f},
std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f},
};
constexpr std::array FirstOrder2DEncoder{
CalcAmbiCoeffs( inv_sqrt2f, 0.0f, inv_sqrt2f),
CalcAmbiCoeffs( inv_sqrt2f, 0.0f, -inv_sqrt2f),
CalcAmbiCoeffs(-inv_sqrt2f, 0.0f, inv_sqrt2f),
CalcAmbiCoeffs(-inv_sqrt2f, 0.0f, -inv_sqrt2f),
CalcAmbiCoeffs(-0.50000000000f, 0.0f, 0.86602540379f),
CalcAmbiCoeffs(-1.00000000000f, 0.0f, 0.00000000000f),
CalcAmbiCoeffs(-0.50000000000f, 0.0f, -0.86602540379f),
CalcAmbiCoeffs( 0.50000000000f, 0.0f, -0.86602540379f),
CalcAmbiCoeffs( 1.00000000000f, 0.0f, 0.00000000000f),
CalcAmbiCoeffs( 0.50000000000f, 0.0f, 0.86602540379f),
};
static_assert(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-order 2D mismatch");

View file

@ -14,9 +14,9 @@ using uint = unsigned int;
* needed will be (o+1)**2, thus zero-order has 1, first-order has 4, second-
* order has 9, third-order has 16, and fourth-order has 25.
*/
inline constexpr auto MaxAmbiOrder = std::uint8_t{3};
inline constexpr auto AmbiChannelsFromOrder(std::size_t order) noexcept -> std::size_t
constexpr auto AmbiChannelsFromOrder(std::size_t order) noexcept -> std::size_t
{ return (order+1) * (order+1); }
inline constexpr auto MaxAmbiOrder = std::uint8_t{3};
inline constexpr auto MaxAmbiChannels = size_t{AmbiChannelsFromOrder(MaxAmbiOrder)};
/* A bitmask of ambisonic channels for 0 to 4th order. This only specifies up
@ -39,20 +39,20 @@ inline constexpr uint AmbiPeriphonicMask{0xfe7ce4};
* representation. This is 2 per each order above zero-order, plus 1 for zero-
* order. Or simply, o*2 + 1.
*/
inline constexpr auto Ambi2DChannelsFromOrder(std::size_t order) noexcept -> std::size_t
constexpr auto Ambi2DChannelsFromOrder(std::size_t order) noexcept -> std::size_t
{ return order*2 + 1; }
inline constexpr auto MaxAmbi2DChannels = std::size_t{Ambi2DChannelsFromOrder(MaxAmbiOrder)};
inline constexpr auto MaxAmbi2DChannels = Ambi2DChannelsFromOrder(MaxAmbiOrder);
/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
* coefficients should be divided by these values to get proper scalings.
*/
struct AmbiScale {
static inline constexpr std::array<float,MaxAmbiChannels> FromN3D{{
static constexpr auto FromN3D = std::array<float,MaxAmbiChannels>{
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
}};
static inline constexpr std::array<float,MaxAmbiChannels> FromSN3D{{
};
static constexpr auto FromSN3D = std::array<float,MaxAmbiChannels>{
1.000000000f, /* ACN 0, sqrt(1) */
1.732050808f, /* ACN 1, sqrt(3) */
1.732050808f, /* ACN 2, sqrt(3) */
@ -69,8 +69,8 @@ struct AmbiScale {
2.645751311f, /* ACN 13, sqrt(7) */
2.645751311f, /* ACN 14, sqrt(7) */
2.645751311f, /* ACN 15, sqrt(7) */
}};
static inline constexpr std::array<float,MaxAmbiChannels> FromFuMa{{
};
static constexpr auto FromFuMa = std::array<float,MaxAmbiChannels>{
1.414213562f, /* ACN 0 (W), sqrt(2) */
1.732050808f, /* ACN 1 (Y), sqrt(3) */
1.732050808f, /* ACN 2 (Z), sqrt(3) */
@ -87,15 +87,15 @@ struct AmbiScale {
2.231093404f, /* ACN 13 (L), sqrt(224/45) */
1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
2.091650066f, /* ACN 15 (P), sqrt(35/8) */
}};
static inline constexpr std::array<float,MaxAmbiChannels> FromUHJ{{
};
static constexpr auto FromUHJ = std::array<float,MaxAmbiChannels>{
1.000000000f, /* ACN 0 (W), sqrt(1) */
1.224744871f, /* ACN 1 (Y), sqrt(3/2) */
1.224744871f, /* ACN 2 (Z), sqrt(3/2) */
1.224744871f, /* ACN 3 (X), sqrt(3/2) */
/* Higher orders not relevant for UHJ. */
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
}};
};
/* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */
static std::array<float,MaxAmbiOrder+1> GetHFOrderScales(const uint src_order,
@ -111,7 +111,7 @@ struct AmbiScale {
};
struct AmbiIndex {
static inline constexpr std::array<std::uint8_t,MaxAmbiChannels> FromFuMa{{
static constexpr auto FromFuMa = std::array<std::uint8_t,MaxAmbiChannels>{
0, /* W */
3, /* X */
1, /* Y */
@ -128,8 +128,8 @@ struct AmbiIndex {
10, /* O */
15, /* P */
9, /* Q */
}};
static inline constexpr std::array<std::uint8_t,MaxAmbi2DChannels> FromFuMa2D{{
};
static constexpr auto FromFuMa2D = std::array<std::uint8_t,MaxAmbi2DChannels>{
0, /* W */
3, /* X */
1, /* Y */
@ -137,23 +137,23 @@ struct AmbiIndex {
4, /* V */
15, /* P */
9, /* Q */
}};
};
static inline constexpr std::array<std::uint8_t,MaxAmbiChannels> FromACN{{
static constexpr auto FromACN = std::array<std::uint8_t,MaxAmbiChannels>{
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15
}};
static inline constexpr std::array<std::uint8_t,MaxAmbi2DChannels> FromACN2D{{
};
static constexpr auto FromACN2D = std::array<std::uint8_t,MaxAmbi2DChannels>{
0, 1,3, 4,8, 9,15
}};
};
static inline constexpr std::array<std::uint8_t,MaxAmbiChannels> OrderFromChannel{{
static constexpr auto OrderFromChannel = std::array<std::uint8_t,MaxAmbiChannels>{
0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3,
}};
static inline constexpr std::array<std::uint8_t,MaxAmbi2DChannels> OrderFrom2DChannel{{
};
static constexpr auto OrderFrom2DChannel = std::array<std::uint8_t,MaxAmbi2DChannels>{
0, 1,1, 2,2, 3,3,
}};
};
};

View file

@ -102,8 +102,8 @@ void BFormatDec::processStablize(const al::span<FloatBufferLine> OutBuffer,
*/
const auto leftout = al::span<float>{OutBuffer[lidx]}.first(SamplesToDo);
const auto rightout = al::span<float>{OutBuffer[ridx]}.first(SamplesToDo);
const al::span<float> mid{al::assume_aligned<16>(mStablizer->MidDirect.data()), SamplesToDo};
const al::span<float> side{al::assume_aligned<16>(mStablizer->Side.data()), SamplesToDo};
const auto mid = al::span{mStablizer->MidDirect}.first(SamplesToDo);
const auto side = al::span{mStablizer->Side}.first(SamplesToDo);
std::transform(leftout.cbegin(), leftout.cend(), rightout.cbegin(), mid.begin(),std::plus{});
std::transform(leftout.cbegin(), leftout.cend(), rightout.cbegin(), side.begin(),std::minus{});
std::fill_n(leftout.begin(), leftout.size(), 0.0f);

View file

@ -13,11 +13,12 @@
#include "devformat.h"
#include "filters/splitter.h"
#include "front_stablizer.h"
#include "opthelpers.h"
using ChannelDec = std::array<float,MaxAmbiChannels>;
class BFormatDec {
class SIMDALIGN BFormatDec {
static constexpr size_t sHFBand{0};
static constexpr size_t sLFBand{1};
static constexpr size_t sNumBands{2};

View file

@ -126,34 +126,36 @@ void bs2b::clear()
history.fill(bs2b::t_last_sample{});
}
void bs2b::cross_feed(float *Left, float *Right, size_t SamplesToDo)
void bs2b::cross_feed(const al::span<float> Left, const al::span<float> Right)
{
const float a0lo{a0_lo};
const float b1lo{b1_lo};
const float a0hi{a0_hi};
const float a1hi{a1_hi};
const float b1hi{b1_hi};
std::array<std::array<float,2>,128> samples{};
al::span<float> lsamples{Left, SamplesToDo};
al::span<float> rsamples{Right, SamplesToDo};
const auto a0lo = a0_lo;
const auto b1lo = b1_lo;
const auto a0hi = a0_hi;
const auto a1hi = a1_hi;
const auto b1hi = b1_hi;
auto lsamples = Left.first(std::min(Left.size(), Right.size()));
auto rsamples = Right.first(lsamples.size());
auto samples = std::array<std::array<float,2>,128>{};
while(!lsamples.empty())
auto leftio = lsamples.begin();
auto rightio = rsamples.begin();
while(auto rem = std::distance(leftio, lsamples.end()))
{
const size_t todo{std::min(samples.size(), lsamples.size())};
const auto todo = std::min<ptrdiff_t>(samples.size(), rem);
/* Process left input */
float z_lo{history[0].lo};
float z_hi{history[0].hi};
std::transform(lsamples.cbegin(), lsamples.cbegin()+ptrdiff_t(todo), samples.begin(),
[a0hi,a1hi,b1hi,a0lo,b1lo,&z_lo,&z_hi](const float x) -> std::array<float,2>
auto z_lo = history[0].lo;
auto z_hi = history[0].hi;
std::transform(leftio, leftio+todo, samples.begin(),
[a0hi,a1hi,b1hi,a0lo,b1lo,&z_lo,&z_hi](const float x) noexcept
{
float y0{a0hi*x + z_hi};
const auto y0 = a0hi*x + z_hi;
z_hi = a1hi*x + b1hi*y0;
float y1{a0lo*x + z_lo};
const auto y1 = a0lo*x + z_lo;
z_lo = b1lo*y1;
return {y0, y1};
return std::array{y0, y1};
});
history[0].lo = z_lo;
history[0].hi = z_hi;
@ -161,28 +163,24 @@ void bs2b::cross_feed(float *Left, float *Right, size_t SamplesToDo)
/* Process right input */
z_lo = history[1].lo;
z_hi = history[1].hi;
std::transform(rsamples.cbegin(), rsamples.cbegin()+ptrdiff_t(todo), samples.begin(),
samples.begin(),
[a0hi,a1hi,b1hi,a0lo,b1lo,&z_lo,&z_hi](const float x, const std::array<float,2> out) -> std::array<float,2>
std::transform(rightio, rightio+todo, samples.cbegin(), samples.begin(),
[a0hi,a1hi,b1hi,a0lo,b1lo,&z_lo,&z_hi](const float x, const std::array<float,2> &out) noexcept
{
float y0{a0lo*x + z_lo};
const auto y0 = a0lo*x + z_lo;
z_lo = b1lo*y0;
float y1{a0hi*x + z_hi};
const auto y1 = a0hi*x + z_hi;
z_hi = a1hi*x + b1hi*y1;
return {out[0]+y0, out[1]+y1};
return std::array{out[0]+y0, out[1]+y1};
});
history[1].lo = z_lo;
history[1].hi = z_hi;
auto iter = std::transform(samples.cbegin(), samples.cbegin()+todo, lsamples.begin(),
leftio = std::transform(samples.cbegin(), samples.cbegin()+todo, leftio,
[](const std::array<float,2> &in) { return in[0]; });
lsamples = {iter, lsamples.end()};
iter = std::transform(samples.cbegin(), samples.cbegin()+todo, rsamples.begin(),
rightio = std::transform(samples.cbegin(), samples.cbegin()+todo, rightio,
[](const std::array<float,2> &in) { return in[1]; });
rsamples = {iter, rsamples.end()};
}
}

View file

@ -27,6 +27,8 @@
#include <array>
#include <cstddef>
#include "alspan.h"
namespace Bs2b {
enum {
@ -81,7 +83,7 @@ struct bs2b {
/* Clear buffer */
void clear();
void cross_feed(float *Left, float *Right, std::size_t SamplesToDo);
void cross_feed(const al::span<float> Left, const al::span<float> Right);
};
} // namespace Bs2b

View file

@ -14,6 +14,7 @@
#include "alnumeric.h"
#include "alspan.h"
#include "bsinc_defs.h"
#include "opthelpers.h"
#include "resampler_limits.h"
@ -21,10 +22,6 @@ namespace {
using uint = unsigned int;
#if __cpp_lib_math_special_functions >= 201603L
using std::cyl_bessel_i;
#else
/* The zero-order modified Bessel function of the first kind, used for the
* Kaiser window.
@ -37,7 +34,7 @@ using std::cyl_bessel_i;
* compounding the rounding and precision error), but it's good enough.
*/
template<typename T, typename U>
U cyl_bessel_i(T nu, U x)
constexpr auto cyl_bessel_i(T nu, U x) -> U
{
if(nu != T{0})
throw std::runtime_error{"cyl_bessel_i: nu != 0"};
@ -61,7 +58,6 @@ U cyl_bessel_i(T nu, U x)
} while(sum != last_sum);
return static_cast<U>(sum);
}
#endif
/* This is the normalized cardinal sine (sinc) function.
*
@ -94,7 +90,7 @@ constexpr double Kaiser(const double beta, const double k, const double besseli_
{
if(!(k >= -1.0 && k <= 1.0))
return 0.0;
return cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
return ::cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
}
/* Calculates the (normalized frequency) transition width of the Kaiser window.
@ -120,73 +116,139 @@ constexpr double CalcKaiserBeta(const double rejection)
struct BSincHeader {
double width{};
double beta{};
double scaleBase{};
double scaleLimit{};
std::array<uint,BSincScaleCount> a{};
std::array<double,BSincScaleCount> a{};
std::array<uint,BSincScaleCount> m{};
uint total_size{};
constexpr BSincHeader(uint Rejection, uint Order) noexcept
: width{CalcKaiserWidth(Rejection, Order)}, beta{CalcKaiserBeta(Rejection)}
, scaleBase{width / 2.0}
constexpr BSincHeader(uint rejection, uint order, uint maxScale) noexcept
: beta{CalcKaiserBeta(rejection)}, scaleBase{CalcKaiserWidth(rejection, order) / 2.0}
, scaleLimit{1.0 / maxScale}
{
uint num_points{Order+1};
const auto base_a = (order+1.0) / 2.0;
for(uint si{0};si < BSincScaleCount;++si)
{
const double scale{lerpd(scaleBase, 1.0, (si+1) / double{BSincScaleCount})};
const uint a_{std::min(static_cast<uint>(num_points / 2.0 / scale), num_points)};
const uint m{2 * a_};
const auto scale = lerpd(scaleBase, 1.0, (si+1u) / double{BSincScaleCount});
a[si] = std::min(base_a/scale, base_a*maxScale);
/* std::ceil() isn't constexpr until C++23, this should behave the
* same.
*/
auto a_ = static_cast<uint>(a[si]);
a_ += (static_cast<double>(a_) != a[si]);
m[si] = a_ * 2u;
a[si] = a_;
total_size += 4 * BSincPhaseCount * ((m+3) & ~3u);
total_size += 4u * BSincPhaseCount * ((m[si]+3u) & ~3u);
}
}
};
/* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop
* at nyquist. Each filter will scale up the order when downsampling, to 23rd
* and 47th order respectively.
* at nyquist. Each filter will scale up to double size when downsampling, to
* 23rd and 47th order respectively.
*/
constexpr BSincHeader bsinc12_hdr{60, 11};
constexpr BSincHeader bsinc24_hdr{60, 23};
constexpr auto bsinc12_hdr = BSincHeader{60, 11, 2};
constexpr auto bsinc24_hdr = BSincHeader{60, 23, 2};
/* 47th order filter (48-point) with an 80dB drop at nyquist. The filter order
* doesn't increase when downsampling.
*/
constexpr auto bsinc48_hdr = BSincHeader{80, 47, 1};
template<const BSincHeader &hdr>
struct BSincFilterArray {
struct SIMDALIGN BSincFilterArray {
alignas(16) std::array<float, hdr.total_size> mTable{};
BSincFilterArray()
{
static constexpr uint BSincPointsMax{(hdr.a[0]*2u + 3u) & ~3u};
static constexpr auto BSincPointsMax = (hdr.m[0]+3u) & ~3u;
static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small");
using filter_type = std::array<std::array<double,BSincPointsMax>,BSincPhaseCount>;
auto filter = std::vector<filter_type>(BSincScaleCount);
const double besseli_0_beta{cyl_bessel_i(0, hdr.beta)};
static constexpr auto besseli_0_beta = ::cyl_bessel_i(0, hdr.beta);
/* Calculate the Kaiser-windowed Sinc filter coefficients for each
* scale and phase index.
*/
for(uint si{0};si < BSincScaleCount;++si)
{
const uint m{hdr.a[si] * 2};
const size_t o{(BSincPointsMax-m) / 2};
const double scale{lerpd(hdr.scaleBase, 1.0, (si+1) / double{BSincScaleCount})};
const double cutoff{scale - (hdr.scaleBase * std::max(1.0, scale*2.0))};
const auto a = static_cast<double>(hdr.a[si]);
const double l{a - 1.0/BSincPhaseCount};
const auto a = hdr.a[si];
const auto m = hdr.m[si];
const auto l = std::floor(m*0.5) - 1.0;
const auto o = size_t{BSincPointsMax-m} / 2u;
const auto scale = lerpd(hdr.scaleBase, 1.0, (si+1u) / double{BSincScaleCount});
/* Calculate an appropriate cutoff frequency. An explanation may be
* in order here.
*
* When up-sampling, or down-sampling by less than the max scaling
* factor (when scale >= scaleLimit), the filter order increases as
* the down-sampling factor is reduced, enabling a consistent
* filter response output.
*
* When down-sampling by more than the max scale factor, the filter
* order stays constant to avoid further increasing the processing
* cost, causing the transition width to increase. This would
* normally be compensated for by reducing the cutoff frequency,
* to keep the transition band under the nyquist frequency and
* avoid aliasing. However, this has the side-effect of attenuating
* more of the original high frequency content, which can be
* significant with more extreme down-sampling scales.
*
* To combat this, we can allow for some aliasing to keep the
* cutoff frequency higher than it would otherwise be. We can allow
* the transition band to "wrap around" the nyquist frequency, so
* the output would have some low-level aliasing that overlays with
* the attenuated frequencies in the transition band. This allows
* the cutoff frequency to remain fixed as the transition width
* increases, until the stop frequency aliases back to the cutoff
* frequency and the transition band becomes fully wrapped over
* itself, at which point the cutoff frequency will lower at half
* the rate the transition width increases.
*
* This has an additional benefit when dealing with typical output
* rates like 44 or 48khz. Since human hearing maxes out at 20khz,
* and these rates handle frequencies up to 22 or 24khz, this lets
* some aliasing get masked. For example, the bsinc24 filter with
* 48khz output has a cutoff of 20khz when down-sampling, and a
* 4khz transition band. When down-sampling by more extreme scales,
* the cutoff frequency can stay at 20khz while the transition
* width doubles before any aliasing noise may become audible.
*
* This is what we do here.
*
* 'max_cutoff` is the upper bound normalized cutoff frequency for
* this scale factor, that aligns with the same absolute frequency
* as nominal resample factors. When up-sampling (scale == 1), the
* cutoff can't be raised further than this, or else it would
* prematurely add audible aliasing noise.
*
* 'width' is the normalized transition width for this scale
* factor.
*
* '(scale - width)*0.5' calculates the cutoff frequency necessary
* for the transition band to fully wrap on itself around the
* nyquist frequency. If this is larger than max_cutoff, the
* transition band is not fully wrapped at this scale and the
* cutoff doesn't need adjustment.
*/
const auto max_cutoff = (0.5 - hdr.scaleBase)*scale;
const auto width = hdr.scaleBase * std::max(hdr.scaleLimit, scale);
const auto cutoff2 = std::min(max_cutoff, (scale - width)*0.5) * 2.0;
for(uint pi{0};pi < BSincPhaseCount;++pi)
{
const double phase{std::floor(l) + (pi/double{BSincPhaseCount})};
const auto phase = l + (pi/double{BSincPhaseCount});
for(uint i{0};i < m;++i)
{
const double x{i - phase};
filter[si][pi][o+i] = Kaiser(hdr.beta, x/l, besseli_0_beta) * cutoff *
Sinc(cutoff*x);
const auto x = static_cast<double>(i) - phase;
filter[si][pi][o+i] = Kaiser(hdr.beta, x/a, besseli_0_beta) * cutoff2 *
Sinc(cutoff2*x);
}
}
}
@ -194,8 +256,8 @@ struct BSincFilterArray {
size_t idx{0};
for(size_t si{0};si < BSincScaleCount;++si)
{
const size_t m{((hdr.a[si]*2) + 3) & ~3u};
const size_t o{(BSincPointsMax-m) / 2};
const auto m = (hdr.m[si]+3_uz) & ~3_uz;
const auto o = size_t{BSincPointsMax-m} / 2u;
/* Write out each phase index's filter and phase delta for this
* quality scale.
@ -282,8 +344,9 @@ struct BSincFilterArray {
[[nodiscard]] constexpr auto getTable() const noexcept { return al::span{mTable}; }
};
const BSincFilterArray<bsinc12_hdr> bsinc12_filter{};
const BSincFilterArray<bsinc24_hdr> bsinc24_filter{};
const auto bsinc12_filter = BSincFilterArray<bsinc12_hdr>{};
const auto bsinc24_filter = BSincFilterArray<bsinc24_hdr>{};
const auto bsinc48_filter = BSincFilterArray<bsinc48_hdr>{};
template<typename T>
constexpr BSincTable GenerateBSincTable(const T &filter)
@ -293,7 +356,7 @@ constexpr BSincTable GenerateBSincTable(const T &filter)
ret.scaleBase = static_cast<float>(hdr.scaleBase);
ret.scaleRange = static_cast<float>(1.0 / (1.0 - hdr.scaleBase));
for(size_t i{0};i < BSincScaleCount;++i)
ret.m[i] = ((hdr.a[i]*2) + 3) & ~3u;
ret.m[i] = (hdr.m[i]+3u) & ~3u;
ret.filterOffset[0] = 0;
for(size_t i{1};i < BSincScaleCount;++i)
ret.filterOffset[i] = ret.filterOffset[i-1] + ret.m[i-1]*4*BSincPhaseCount;
@ -305,3 +368,4 @@ constexpr BSincTable GenerateBSincTable(const T &filter)
const BSincTable gBSinc12{GenerateBSincTable(bsinc12_filter)};
const BSincTable gBSinc24{GenerateBSincTable(bsinc24_filter)};
const BSincTable gBSinc48{GenerateBSincTable(bsinc48_filter)};

View file

@ -5,7 +5,7 @@
#include "alspan.h"
#include "bsinc_defs.h"
#include "opthelpers.h"
struct BSincTable {
float scaleBase, scaleRange;
@ -14,7 +14,8 @@ struct BSincTable {
al::span<const float> Tab;
};
extern const BSincTable gBSinc12;
extern const BSincTable gBSinc24;
DECL_HIDDEN extern const BSincTable gBSinc12;
DECL_HIDDEN extern const BSincTable gBSinc24;
DECL_HIDDEN extern const BSincTable gBSinc48;
#endif /* CORE_BSINC_TABLES_H */

View file

@ -1,10 +1,8 @@
#ifndef CORE_BUFFER_STORAGE_H
#define CORE_BUFFER_STORAGE_H
#include <atomic>
#include <cstddef>
#include "alnumeric.h"
#include "alspan.h"
#include "ambidefs.h"
#include "storage_formats.h"
@ -34,7 +32,7 @@ constexpr bool Is2DAmbisonic(FmtChannels chans) noexcept
}
using CallbackType = int(*)(void*, void*, int);
using CallbackType = int(*)(void*, void*, int) noexcept;
struct BufferStorage {
CallbackType mCallback{nullptr};

View file

@ -33,21 +33,16 @@ ContextBase::~ContextBase()
if(mAsyncEvents)
{
size_t count{0};
auto evt_vec = mAsyncEvents->getReadVector();
if(evt_vec.first.len > 0)
for(auto &evt : mAsyncEvents->getReadVector())
{
std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf)),
evt_vec.first.len);
count += evt_vec.first.len;
}
if(evt_vec.second.len > 0)
{
std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf)),
evt_vec.second.len);
count += evt_vec.second.len;
if(evt.len > 0)
{
std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt.buf)), evt.len);
count += evt.len;
}
}
if(count > 0)
TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s");
TRACE("Destructed {} orphaned event{}", count, (count==1)?"":"s");
mAsyncEvents->readAdvance(count);
}
}
@ -72,7 +67,7 @@ void ContextBase::allocVoiceProps()
{
static constexpr size_t clustersize{std::tuple_size_v<VoicePropsCluster::element_type>};
TRACE("Increasing allocated voice properties to %zu\n",
TRACE("Increasing allocated voice properties to {}",
(mVoicePropClusters.size()+1) * clustersize);
auto clusterptr = std::make_unique<VoicePropsCluster::element_type>();
@ -104,7 +99,7 @@ void ContextBase::allocVoices(size_t addcount)
if(addcount >= std::numeric_limits<int>::max()/clustersize - mVoiceClusters.size())
throw std::runtime_error{"Allocating too many voices"};
const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize};
TRACE("Increasing allocated voices to %zu\n", totalcount);
TRACE("Increasing allocated voices to {}", totalcount);
while(addcount)
{
@ -127,7 +122,7 @@ void ContextBase::allocEffectSlotProps()
{
static constexpr size_t clustersize{std::tuple_size_v<EffectSlotPropsCluster::element_type>};
TRACE("Increasing allocated effect slot properties to %zu\n",
TRACE("Increasing allocated effect slot properties to {}",
(mEffectSlotPropClusters.size()+1) * clustersize);
auto clusterptr = std::make_unique<EffectSlotPropsCluster::element_type>();
@ -157,7 +152,7 @@ EffectSlot *ContextBase::getEffectSlot()
if(1 >= std::numeric_limits<int>::max()/clusterptr->size() - mEffectSlotClusters.size())
throw std::runtime_error{"Allocating too many effect slots"};
const size_t totalcount{(mEffectSlotClusters.size()+1) * clusterptr->size()};
TRACE("Increasing allocated effect slots to %zu\n", totalcount);
TRACE("Increasing allocated effect slots to {}", totalcount);
mEffectSlotClusters.emplace_back(std::move(clusterptr));
return mEffectSlotClusters.back()->data();
@ -168,7 +163,7 @@ void ContextBase::allocContextProps()
{
static constexpr size_t clustersize{std::tuple_size_v<ContextPropsCluster::element_type>};
TRACE("Increasing allocated context properties to %zu\n",
TRACE("Increasing allocated context properties to {}",
(mContextPropClusters.size()+1) * clustersize);
auto clusterptr = std::make_unique<ContextPropsCluster::element_type>();

View file

@ -1,6 +1,8 @@
#ifndef CORE_CONTEXT_H
#define CORE_CONTEXT_H
#include "config.h"
#include <array>
#include <atomic>
#include <bitset>
@ -9,7 +11,6 @@
#include <thread>
#include <vector>
#include "almalloc.h"
#include "alsem.h"
#include "alspan.h"
#include "async_event.h"
@ -53,19 +54,22 @@ struct ContextProps {
float DopplerFactor;
float DopplerVelocity;
float SpeedOfSound;
#if ALSOFT_EAX
float DistanceFactor;
#endif
bool SourceDistanceModel;
DistanceModel mDistanceModel;
std::atomic<ContextProps*> next;
std::atomic<ContextProps*> next{};
};
struct ContextParams {
/* Pointer to the most recent property values that are awaiting an update. */
std::atomic<ContextProps*> ContextUpdate{nullptr};
alu::Vector Position{};
alu::Vector Position;
alu::Matrix Matrix{alu::Matrix::Identity()};
alu::Vector Velocity{};
alu::Vector Velocity;
float Gain{1.0f};
float MetersPerUnit{1.0f};
@ -113,7 +117,7 @@ struct ContextBase {
ContextParams mParams;
using VoiceArray = al::FlexArray<Voice*>;
al::atomic_unique_ptr<VoiceArray> mVoices{};
al::atomic_unique_ptr<VoiceArray> mVoices;
std::atomic<size_t> mActiveVoiceCount{};
void allocVoices(size_t addcount);
@ -172,10 +176,10 @@ struct ContextBase {
std::vector<ContextPropsCluster> mContextPropClusters;
ContextBase(DeviceBase *device);
explicit ContextBase(DeviceBase *device);
ContextBase(const ContextBase&) = delete;
ContextBase& operator=(const ContextBase&) = delete;
~ContextBase();
virtual ~ContextBase();
};
#endif /* CORE_CONTEXT_H */

View file

@ -169,10 +169,11 @@ void Multi2Mono(uint chanmask, const size_t step, const float scale, const al::s
SampleConverterPtr SampleConverter::Create(DevFmtType srcType, DevFmtType dstType, size_t numchans,
uint srcRate, uint dstRate, Resampler resampler)
{
SampleConverterPtr converter;
if(numchans < 1 || srcRate < 1 || dstRate < 1)
return nullptr;
return converter;
SampleConverterPtr converter{new(FamCount(numchans)) SampleConverter{numchans}};
converter = SampleConverterPtr{new(FamCount(numchans)) SampleConverter{numchans}};
converter->mSrcType = srcType;
converter->mDstType = dstType;
converter->mSrcTypeSize = BytesFromDevFmt(srcType);

View file

@ -24,7 +24,7 @@ struct SampleConverter {
uint mFracOffset{};
uint mIncrement{};
InterpState mState{};
InterpState mState;
ResamplerFunc mResample{};
alignas(16) FloatBufferLine mSrcSamples{};
@ -35,7 +35,7 @@ struct SampleConverter {
};
al::FlexArray<ChanSamples> mChan;
SampleConverter(size_t numchans) : mChan{numchans} { }
explicit SampleConverter(size_t numchans) : mChan{numchans} { }
[[nodiscard]] auto convert(const void **src, uint *srcframes, void *dst, uint dstframes) -> uint;
[[nodiscard]] auto convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes) -> uint;

View file

@ -1,5 +1,6 @@
#include "config.h"
#include "config_simd.h"
#include "cpu_caps.h"
@ -23,8 +24,6 @@
#include <string>
int CPUCapFlags{0};
namespace {
#if defined(HAVE_GCC_GET_CPUID) \
@ -111,22 +110,22 @@ std::optional<CPUInfo> GetCPUInfo()
#else
/* Assume support for whatever's supported if we can't check for it */
#if defined(HAVE_SSE4_1)
#if HAVE_SSE4_1
#warning "Assuming SSE 4.1 run-time support!"
ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
#elif defined(HAVE_SSE3)
#elif HAVE_SSE3
#warning "Assuming SSE 3 run-time support!"
ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
#elif defined(HAVE_SSE2)
#elif HAVE_SSE2
#warning "Assuming SSE 2 run-time support!"
ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2;
#elif defined(HAVE_SSE)
#elif HAVE_SSE
#warning "Assuming SSE run-time support!"
ret.mCaps |= CPU_CAP_SSE;
#endif
#endif /* CAN_GET_CPUID */
#ifdef HAVE_NEON
#if HAVE_NEON
#ifdef __ARM_NEON
ret.mCaps |= CPU_CAP_NEON;
#elif defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64))

View file

@ -5,7 +5,7 @@
#include <string>
extern int CPUCapFlags;
inline int CPUCapFlags{0};
enum {
CPU_CAP_SSE = 1<<0,
CPU_CAP_SSE2 = 1<<1,

View file

@ -15,7 +15,7 @@
*
* <https://forums.nesdev.org/viewtopic.php?p=251534#p251534>
*
* Additional changes were made here, the most obvious being that is has full
* Additional changes were made here, the most obvious being that it has full
* floating-point precision instead of 11-bit fixed-point, but also an offset
* adjustment for the coefficients to better preserve phase.
*/
@ -26,9 +26,9 @@ auto GetCoeff(double idx) noexcept -> double
{
const double k{0.5 + idx};
if(k > 512.0) return 0.0;
const double s{ std::sin(al::numbers::pi*1.280/1024 * k)};
const double t{(std::cos(al::numbers::pi*2.000/1023 * k) - 1.0) * 0.50};
const double u{(std::cos(al::numbers::pi*4.000/1023 * k) - 1.0) * 0.08};
const double s{ std::sin(al::numbers::pi*1.280/1024.0 * k)};
const double t{(std::cos(al::numbers::pi*2.000/1023.0 * k) - 1.0) * 0.50};
const double u{(std::cos(al::numbers::pi*4.000/1023.0 * k) - 1.0) * 0.08};
return s * (t + u + 1.0) / k;
}
@ -70,17 +70,20 @@ GaussianTable::GaussianTable()
SplineTable::SplineTable()
{
static constexpr auto third = 1.0/3.0;
static constexpr auto sixth = 1.0/6.0;
/* This filter table is based on a Catmull-Rom spline. It retains more of
* the original high-frequency content, at the cost of increased harmonics.
*/
for(std::size_t pi{0};pi < CubicPhaseCount;++pi)
{
const double mu{static_cast<double>(pi) / double{CubicPhaseCount}};
const double mu2{mu*mu}, mu3{mu2*mu};
mTable[pi].mCoeffs[0] = static_cast<float>(-0.5*mu3 + mu2 + -0.5*mu);
mTable[pi].mCoeffs[1] = static_cast<float>( 1.5*mu3 + -2.5*mu2 + 1.0);
mTable[pi].mCoeffs[2] = static_cast<float>(-1.5*mu3 + 2.0*mu2 + 0.5*mu);
mTable[pi].mCoeffs[3] = static_cast<float>( 0.5*mu3 + -0.5*mu2);
const auto mu = static_cast<double>(pi) / double{CubicPhaseCount};
const auto mu2 = mu*mu;
const auto mu3 = mu*mu2;
mTable[pi].mCoeffs[0] = static_cast<float>( -third*mu + 0.5*mu2 - sixth*mu3);
mTable[pi].mCoeffs[1] = static_cast<float>(1.0 - 0.5*mu - mu2 + 0.5*mu3);
mTable[pi].mCoeffs[2] = static_cast<float>( mu + 0.5*mu2 - 0.5*mu3);
mTable[pi].mCoeffs[3] = static_cast<float>( -sixth*mu + sixth*mu3);
}
for(std::size_t pi{0};pi < CubicPhaseCount-1;++pi)
@ -91,7 +94,7 @@ SplineTable::SplineTable()
mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3];
}
const std::size_t pi{CubicPhaseCount - 1};
static constexpr auto pi = std::size_t{CubicPhaseCount - 1};
mTable[pi].mDeltas[0] = 0.0f - mTable[pi].mCoeffs[0];
mTable[pi].mDeltas[1] = mTable[0].mCoeffs[0] - mTable[pi].mCoeffs[1];
mTable[pi].mDeltas[2] = mTable[0].mCoeffs[1] - mTable[pi].mCoeffs[2];

View file

@ -5,9 +5,10 @@
#include <cstddef>
#include "cubic_defs.h"
#include "opthelpers.h"
struct CubicTable {
struct SIMDALIGN CubicTable {
std::array<CubicCoefficients,CubicPhaseCount> mTable{};
};

View file

@ -3,7 +3,7 @@
#include "dbus_wrap.h"
#ifdef HAVE_DYNLOAD
#if HAVE_DYNLOAD
#include <mutex>
#include <type_traits>
@ -18,7 +18,7 @@ void PrepareDBus()
dbus_handle = LoadLib(libname);
if(!dbus_handle)
{
WARN("Failed to load %s\n", libname);
WARN("Failed to load {}", libname);
return;
}
@ -28,7 +28,7 @@ void PrepareDBus()
load_func(p##x, #x); \
if(!p##x) \
{ \
WARN("Failed to load function %s\n", #x); \
WARN("Failed to load function {}", #x); \
CloseLib(dbus_handle); \
dbus_handle = nullptr; \
return; \

View file

@ -7,7 +7,7 @@
#include "dynload.h"
#ifdef HAVE_DYNLOAD
#if HAVE_DYNLOAD
#include <mutex>
@ -63,7 +63,7 @@ inline auto HasDBus()
#else
constexpr bool HasDBus() noexcept { return true; }
#endif /* HAVE_DYNLOAD */
#endif
namespace dbus {

View file

@ -3,6 +3,11 @@
#include "devformat.h"
#include <string_view>
namespace {
using namespace std::string_view_literals;
} // namespace
uint BytesFromDevFmt(DevFmtType type) noexcept
{
@ -36,34 +41,34 @@ uint ChannelsFromDevFmt(DevFmtChannels chans, uint ambiorder) noexcept
return 0;
}
const char *DevFmtTypeString(DevFmtType type) noexcept
auto DevFmtTypeString(DevFmtType type) noexcept -> std::string_view
{
switch(type)
{
case DevFmtByte: return "Int8";
case DevFmtUByte: return "UInt8";
case DevFmtShort: return "Int16";
case DevFmtUShort: return "UInt16";
case DevFmtInt: return "Int32";
case DevFmtUInt: return "UInt32";
case DevFmtFloat: return "Float32";
case DevFmtByte: return "Int8"sv;
case DevFmtUByte: return "UInt8"sv;
case DevFmtShort: return "Int16"sv;
case DevFmtUShort: return "UInt16"sv;
case DevFmtInt: return "Int32"sv;
case DevFmtUInt: return "UInt32"sv;
case DevFmtFloat: return "Float32"sv;
}
return "(unknown type)";
return "(unknown type)"sv;
}
const char *DevFmtChannelsString(DevFmtChannels chans) noexcept
auto DevFmtChannelsString(DevFmtChannels chans) noexcept -> std::string_view
{
switch(chans)
{
case DevFmtMono: return "Mono";
case DevFmtStereo: return "Stereo";
case DevFmtQuad: return "Quadraphonic";
case DevFmtX51: return "5.1 Surround";
case DevFmtX61: return "6.1 Surround";
case DevFmtX71: return "7.1 Surround";
case DevFmtX714: return "7.1.4 Surround";
case DevFmtX7144: return "7.1.4.4 Surround";
case DevFmtX3D71: return "3D7.1 Surround";
case DevFmtAmbi3D: return "Ambisonic 3D";
case DevFmtMono: return "Mono"sv;
case DevFmtStereo: return "Stereo"sv;
case DevFmtQuad: return "Quadraphonic"sv;
case DevFmtX51: return "5.1 Surround"sv;
case DevFmtX61: return "6.1 Surround"sv;
case DevFmtX71: return "7.1 Surround"sv;
case DevFmtX714: return "7.1.4 Surround"sv;
case DevFmtX7144: return "7.1.4.4 Surround"sv;
case DevFmtX3D71: return "3D7.1 Surround"sv;
case DevFmtAmbi3D: return "Ambisonic 3D"sv;
}
return "(unknown channels)";
return "(unknown channels)"sv;
}

View file

@ -3,6 +3,7 @@
#include <cstdint>
#include <cstddef>
#include <string_view>
using uint = unsigned int;
@ -108,8 +109,8 @@ uint ChannelsFromDevFmt(DevFmtChannels chans, uint ambiorder) noexcept;
inline uint FrameSizeFromDevFmt(DevFmtChannels chans, DevFmtType type, uint ambiorder) noexcept
{ return ChannelsFromDevFmt(chans, ambiorder) * BytesFromDevFmt(type); }
const char *DevFmtTypeString(DevFmtType type) noexcept;
const char *DevFmtChannelsString(DevFmtChannels chans) noexcept;
auto DevFmtTypeString(DevFmtType type) noexcept -> std::string_view;
auto DevFmtChannelsString(DevFmtChannels chans) noexcept -> std::string_view;
enum class DevAmbiLayout : bool {
FuMa,

View file

@ -9,9 +9,6 @@
#include "mastering.h"
static_assert(std::atomic<std::chrono::nanoseconds>::is_always_lock_free);
DeviceBase::DeviceBase(DeviceType type)
: Type{type}, mContexts{al::FlexArray<ContextBase*>::Create(0)}
{

View file

@ -18,6 +18,7 @@
#include "devformat.h"
#include "filters/nfc.h"
#include "flexarray.h"
#include "fmt/core.h"
#include "intrusive_ptr.h"
#include "mixer/hrtfdefs.h"
#include "opthelpers.h"
@ -80,14 +81,14 @@ struct DistanceComp {
static constexpr uint MaxDelay{1024};
struct ChanData {
al::span<float> Buffer{}; /* Valid size is [0...MaxDelay). */
al::span<float> Buffer; /* Valid size is [0...MaxDelay). */
float Gain{1.0f};
};
std::array<ChanData,MaxOutputChannels> mChannels;
al::FlexArray<float,16> mSamples;
DistanceComp(std::size_t count) : mSamples{count} { }
explicit DistanceComp(std::size_t count) : mSamples{count} { }
static std::unique_ptr<DistanceComp> Create(std::size_t numsamples)
{ return std::unique_ptr<DistanceComp>{new(FamCount(numsamples)) DistanceComp{numsamples}}; }
@ -179,13 +180,16 @@ enum class DeviceState : std::uint8_t {
Playing
};
struct DeviceBase {
/* NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) */
struct SIMDALIGN DeviceBase {
std::atomic<bool> Connected{true};
const DeviceType Type{};
uint Frequency{};
uint UpdateSize{};
uint BufferSize{};
std::string mDeviceName;
uint mSampleRate{};
uint mUpdateSize{};
uint mBufferSize{};
DevFmtChannels FmtChans{};
DevFmtType FmtType{};
@ -199,10 +203,8 @@ struct DeviceBase {
DevAmbiLayout mAmbiLayout{DevAmbiLayout::Default};
DevAmbiScaling mAmbiScale{DevAmbiScaling::Default};
std::string DeviceName;
// Device flags
std::bitset<DeviceFlagsCount> Flags{};
std::bitset<DeviceFlagsCount> Flags;
DeviceState mDeviceState{DeviceState::Unprepared};
uint NumAuxSends{};
@ -220,8 +222,13 @@ struct DeviceBase {
*/
NfcFilter mNFCtrlFilter{};
using seconds32 = std::chrono::duration<int32_t>;
using nanoseconds32 = std::chrono::duration<int32_t, std::nano>;
std::atomic<uint> mSamplesDone{0u};
std::atomic<std::chrono::nanoseconds> mClockBase{std::chrono::nanoseconds{}};
/* Split the clock to avoid a 64-bit atomic for certain 32-bit targets. */
std::atomic<seconds32> mClockBaseSec{seconds32{}};
std::atomic<nanoseconds32> mClockBaseNSec{nanoseconds32{}};
std::chrono::nanoseconds FixedLatency{0};
AmbiRotateMatrix mAmbiRotateMatrix{};
@ -288,11 +295,6 @@ struct DeviceBase {
al::atomic_unique_ptr<al::FlexArray<ContextBase*>> mContexts;
DeviceBase(DeviceType type);
DeviceBase(const DeviceBase&) = delete;
DeviceBase& operator=(const DeviceBase&) = delete;
~DeviceBase();
[[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromDevFmt(FmtType); }
[[nodiscard]] auto channelsFromFmt() const noexcept -> uint { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); }
[[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint { return bytesFromFmt() * channelsFromFmt(); }
@ -314,9 +316,8 @@ struct DeviceBase {
/* Increment the mix count at the start of mixing and writing clock
* info (lsb should be 1).
*/
auto mixCount = mMixCount.load(std::memory_order_relaxed);
mMixCount.store(++mixCount, std::memory_order_release);
return MixLock{this, ++mixCount};
const auto oldCount = mMixCount.fetch_add(1u, std::memory_order_acq_rel);
return MixLock{this, oldCount+2};
}
/** Waits for the mixer to not be mixing or updating the clock. */
@ -337,8 +338,9 @@ struct DeviceBase {
using std::chrono::seconds;
using std::chrono::nanoseconds;
auto ns = nanoseconds{seconds{mSamplesDone.load(std::memory_order_relaxed)}} / Frequency;
return mClockBase.load(std::memory_order_relaxed) + ns;
auto ns = nanoseconds{seconds{mSamplesDone.load(std::memory_order_relaxed)}} / mSampleRate;
return nanoseconds{mClockBaseNSec.load(std::memory_order_relaxed)}
+ mClockBaseSec.load(std::memory_order_relaxed) + ns;
}
void ProcessHrtf(const std::size_t SamplesToDo);
@ -347,19 +349,18 @@ struct DeviceBase {
void ProcessUhj(const std::size_t SamplesToDo);
void ProcessBs2b(const std::size_t SamplesToDo);
inline void postProcess(const std::size_t SamplesToDo)
void postProcess(const std::size_t SamplesToDo)
{ if(PostProcess) LIKELY (this->*PostProcess)(SamplesToDo); }
void renderSamples(const al::span<float*> outBuffers, const uint numSamples);
void renderSamples(const al::span<void*> outBuffers, const uint numSamples);
void renderSamples(void *outBuffer, const uint numSamples, const std::size_t frameStep);
/* Caller must lock the device state, and the mixer must not be running. */
#ifdef __MINGW32__
[[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]]
#else
[[gnu::format(printf,2,3)]]
#endif
void handleDisconnect(const char *msg, ...);
void doDisconnect(std::string msg);
template<typename ...Args>
void handleDisconnect(fmt::format_string<Args...> fmt, Args&& ...args)
{ doDisconnect(fmt::format(std::move(fmt), std::forward<Args>(args)...)); }
/**
* Returns the index for the given channel name (e.g. FrontCenter), or
@ -370,6 +371,14 @@ struct DeviceBase {
private:
uint renderSamples(const uint numSamples);
protected:
explicit DeviceBase(DeviceType type);
~DeviceBase();
public:
DeviceBase(const DeviceBase&) = delete;
DeviceBase& operator=(const DeviceBase&) = delete;
};
/* Must be less than 15 characters (16 including terminating null) for

View file

@ -8,6 +8,7 @@
#include "alspan.h"
#include "core/bufferline.h"
#include "intrusive_ptr.h"
#include "opthelpers.h"
struct BufferStorage;
struct ContextBase;
@ -193,7 +194,7 @@ struct EffectTarget {
RealMixParams *RealOut;
};
struct EffectState : public al::intrusive_ref<EffectState> {
struct SIMDALIGN EffectState : public al::intrusive_ref<EffectState> {
al::span<FloatBufferLine> mOutTarget;

View file

@ -43,7 +43,7 @@ struct EffectSlotProps {
al::intrusive_ptr<EffectState> State;
std::atomic<EffectSlotProps*> next;
std::atomic<EffectSlotProps*> next{};
};
@ -64,7 +64,7 @@ struct EffectSlot {
EffectSlot *Target{nullptr};
EffectSlotType EffectType{EffectSlotType::None};
EffectProps mEffectProps{};
EffectProps mEffectProps;
al::intrusive_ptr<EffectState> mEffectState;
float RoomRolloff{0.0f}; /* Added to the source's room rolloff, not multiplied. */

View file

@ -3,30 +3,9 @@
#include "except.h"
#include <cstdio>
#include <cstdarg>
#include "opthelpers.h"
namespace al {
base_exception::~base_exception() = default;
void base_exception::setMessage(const char *msg, std::va_list args)
{
/* NOLINTBEGIN(*-array-to-pointer-decay) */
std::va_list args2;
va_copy(args2, args);
int msglen{std::vsnprintf(nullptr, 0, msg, args)};
if(msglen > 0) LIKELY
{
mMessage.resize(static_cast<size_t>(msglen)+1);
std::vsnprintf(mMessage.data(), mMessage.length(), msg, args2);
mMessage.pop_back();
}
va_end(args2);
/* NOLINTEND(*-array-to-pointer-decay) */
}
} // namespace al

View file

@ -1,10 +1,9 @@
#ifndef CORE_EXCEPT_H
#define CORE_EXCEPT_H
#include <cstdarg>
#include <exception>
#include <string>
#include <utility>
#include <type_traits>
namespace al {
@ -12,11 +11,10 @@ namespace al {
class base_exception : public std::exception {
std::string mMessage;
protected:
auto setMessage(const char *msg, std::va_list args) -> void;
public:
base_exception() = default;
template<typename T, std::enable_if_t<std::is_constructible_v<std::string,T>,bool> = true>
explicit base_exception(T&& msg) : mMessage{std::forward<T>(msg)} { }
base_exception(const base_exception&) = default;
base_exception(base_exception&&) = default;
~base_exception() override;

View file

@ -120,7 +120,7 @@ public:
const al::span<Real> dst);
/* Rather hacky. It's just here to support "manual" processing. */
[[nodiscard]] auto getComponents() const noexcept -> std::pair<Real,Real> { return {mZ1, mZ2}; }
[[nodiscard]] auto getComponents() const noexcept -> std::array<Real,2> { return {{mZ1,mZ2}}; }
void setComponents(Real z1, Real z2) noexcept { mZ1 = z1; mZ2 = z2; }
[[nodiscard]] auto processOne(const Real in, Real &z1, Real &z2) const noexcept -> Real
{

View file

@ -5,8 +5,6 @@
#include <algorithm>
#include "opthelpers.h"
/* Near-field control filters are the basis for handling the near-field effect.
* The near-field effect is a bass-boost present in the directional components
@ -48,29 +46,26 @@
namespace {
constexpr std::array B{
std::array{ 0.0f, 0.0f, 0.0f, 0.0f},
std::array{ 1.0f, 0.0f, 0.0f, 0.0f},
std::array{ 3.0f, 3.0f, 0.0f, 0.0f},
std::array{3.6778f, 6.4595f, 2.3222f, 0.0f},
std::array{4.2076f, 11.4877f, 5.7924f, 9.1401f}
};
constexpr auto B1 = std::array{ 1.0f};
constexpr auto B2 = std::array{ 3.0f, 3.0f};
constexpr auto B3 = std::array{3.6778f, 6.4595f, 2.3222f};
constexpr auto B4 = std::array{4.2076f, 11.4877f, 5.7924f, 9.1401f};
NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept
{
NfcFilter1 nfc{};
auto nfc = NfcFilter1{};
/* Calculate bass-cut coefficients. */
float r{0.5f * w1};
float b_00{B[1][0] * r};
float g_0{1.0f + b_00};
auto r = 0.5f * w1;
auto b_00 = B1[0] * r;
auto g_0 = 1.0f + b_00;
nfc.base_gain = 1.0f / g_0;
nfc.a1 = 2.0f * b_00 / g_0;
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_00 = B[1][0] * r;
b_00 = B1[0] * r;
g_0 = 1.0f + b_00;
nfc.gain = nfc.base_gain * g_0;
@ -81,9 +76,9 @@ NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept
void NfcFilterAdjust1(NfcFilter1 *nfc, const float w0) noexcept
{
const float r{0.5f * w0};
const float b_00{B[1][0] * r};
const float g_0{1.0f + b_00};
const auto r = 0.5f * w0;
const auto b_00 = B1[0] * r;
const auto g_0 = 1.0f + b_00;
nfc->gain = nfc->base_gain * g_0;
nfc->b1 = 2.0f * b_00 / g_0;
@ -92,13 +87,13 @@ void NfcFilterAdjust1(NfcFilter1 *nfc, const float w0) noexcept
NfcFilter2 NfcFilterCreate2(const float w0, const float w1) noexcept
{
NfcFilter2 nfc{};
auto nfc = NfcFilter2{};
/* Calculate bass-cut coefficients. */
float r{0.5f * w1};
float b_10{B[2][0] * r};
float b_11{B[2][1] * r * r};
float g_1{1.0f + b_10 + b_11};
auto r = 0.5f * w1;
auto b_10 = B2[0] * r;
auto b_11 = B2[1] * (r*r);
auto g_1 = 1.0f + b_10 + b_11;
nfc.base_gain = 1.0f / g_1;
nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
@ -106,8 +101,8 @@ NfcFilter2 NfcFilterCreate2(const float w0, const float w1) noexcept
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_10 = B[2][0] * r;
b_11 = B[2][1] * r * r;
b_10 = B2[0] * r;
b_11 = B2[1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc.gain = nfc.base_gain * g_1;
@ -119,10 +114,10 @@ NfcFilter2 NfcFilterCreate2(const float w0, const float w1) noexcept
void NfcFilterAdjust2(NfcFilter2 *nfc, const float w0) noexcept
{
const float r{0.5f * w0};
const float b_10{B[2][0] * r};
const float b_11{B[2][1] * r * r};
const float g_1{1.0f + b_10 + b_11};
const auto r = 0.5f * w0;
const auto b_10 = B2[0] * r;
const auto b_11 = B2[1] * (r*r);
const auto g_1 = 1.0f + b_10 + b_11;
nfc->gain = nfc->base_gain * g_1;
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
@ -132,15 +127,15 @@ void NfcFilterAdjust2(NfcFilter2 *nfc, const float w0) noexcept
NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept
{
NfcFilter3 nfc{};
auto nfc = NfcFilter3{};
/* Calculate bass-cut coefficients. */
float r{0.5f * w1};
float b_10{B[3][0] * r};
float b_11{B[3][1] * r * r};
float b_00{B[3][2] * r};
float g_1{1.0f + b_10 + b_11};
float g_0{1.0f + b_00};
auto r = 0.5f * w1;
auto b_10 = B3[0] * r;
auto b_11 = B3[1] * (r*r);
auto b_00 = B3[2] * r;
auto g_1 = 1.0f + b_10 + b_11;
auto g_0 = 1.0f + b_00;
nfc.base_gain = 1.0f / (g_1 * g_0);
nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
@ -149,9 +144,9 @@ NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_10 = B[3][0] * r;
b_11 = B[3][1] * r * r;
b_00 = B[3][2] * r;
b_10 = B3[0] * r;
b_11 = B3[1] * (r*r);
b_00 = B3[2] * r;
g_1 = 1.0f + b_10 + b_11;
g_0 = 1.0f + b_00;
@ -165,12 +160,12 @@ NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept
void NfcFilterAdjust3(NfcFilter3 *nfc, const float w0) noexcept
{
const float r{0.5f * w0};
const float b_10{B[3][0] * r};
const float b_11{B[3][1] * r * r};
const float b_00{B[3][2] * r};
const float g_1{1.0f + b_10 + b_11};
const float g_0{1.0f + b_00};
const auto r = 0.5f * w0;
const auto b_10 = B3[0] * r;
const auto b_11 = B3[1] * (r*r);
const auto b_00 = B3[2] * r;
const auto g_1 = 1.0f + b_10 + b_11;
const auto g_0 = 1.0f + b_00;
nfc->gain = nfc->base_gain * (g_1 * g_0);
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
@ -181,16 +176,16 @@ void NfcFilterAdjust3(NfcFilter3 *nfc, const float w0) noexcept
NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept
{
NfcFilter4 nfc{};
auto nfc = NfcFilter4{};
/* Calculate bass-cut coefficients. */
float r{0.5f * w1};
float b_10{B[4][0] * r};
float b_11{B[4][1] * r * r};
float b_00{B[4][2] * r};
float b_01{B[4][3] * r * r};
float g_1{1.0f + b_10 + b_11};
float g_0{1.0f + b_00 + b_01};
auto r = 0.5f * w1;
auto b_10 = B4[0] * r;
auto b_11 = B4[1] * (r*r);
auto b_00 = B4[2] * r;
auto b_01 = B4[3] * (r*r);
auto g_1 = 1.0f + b_10 + b_11;
auto g_0 = 1.0f + b_00 + b_01;
nfc.base_gain = 1.0f / (g_1 * g_0);
nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
@ -200,10 +195,10 @@ NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_10 = B[4][0] * r;
b_11 = B[4][1] * r * r;
b_00 = B[4][2] * r;
b_01 = B[4][3] * r * r;
b_10 = B4[0] * r;
b_11 = B4[1] * (r*r);
b_00 = B4[2] * r;
b_01 = B4[3] * (r*r);
g_1 = 1.0f + b_10 + b_11;
g_0 = 1.0f + b_00 + b_01;
@ -218,13 +213,13 @@ NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept
void NfcFilterAdjust4(NfcFilter4 *nfc, const float w0) noexcept
{
const float r{0.5f * w0};
const float b_10{B[4][0] * r};
const float b_11{B[4][1] * r * r};
const float b_00{B[4][2] * r};
const float b_01{B[4][3] * r * r};
const float g_1{1.0f + b_10 + b_11};
const float g_0{1.0f + b_00 + b_01};
const auto r = 0.5f * w0;
const auto b_10 = B4[0] * r;
const auto b_11 = B4[1] * (r*r);
const auto b_00 = B4[2] * r;
const auto b_01 = B4[3] * (r*r);
const auto g_1 = 1.0f + b_10 + b_11;
const auto g_0 = 1.0f + b_00 + b_01;
nfc->gain = nfc->base_gain * (g_1 * g_0);
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;

View file

@ -17,7 +17,7 @@ class BandSplitterR {
public:
BandSplitterR() = default;
BandSplitterR(const BandSplitterR&) = default;
BandSplitterR(Real f0norm) { init(f0norm); }
explicit BandSplitterR(Real f0norm) { init(f0norm); }
BandSplitterR& operator=(const BandSplitterR&) = default;
void init(Real f0norm);

View file

@ -1,79 +0,0 @@
#include "config.h"
#include "fmt_traits.h"
namespace al {
const std::array<int16_t,256> muLawDecompressionTable{{
-32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
-23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
-15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
-11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
-7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
-5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
-3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
-2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
-1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
-1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
-876, -844, -812, -780, -748, -716, -684, -652,
-620, -588, -556, -524, -492, -460, -428, -396,
-372, -356, -340, -324, -308, -292, -276, -260,
-244, -228, -212, -196, -180, -164, -148, -132,
-120, -112, -104, -96, -88, -80, -72, -64,
-56, -48, -40, -32, -24, -16, -8, 0,
32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
876, 844, 812, 780, 748, 716, 684, 652,
620, 588, 556, 524, 492, 460, 428, 396,
372, 356, 340, 324, 308, 292, 276, 260,
244, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64,
56, 48, 40, 32, 24, 16, 8, 0
}};
const std::array<int16_t,256> aLawDecompressionTable{{
-5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
-7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
-2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
-3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
-22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
-30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
-11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472,
-15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
-344, -328, -376, -360, -280, -264, -312, -296,
-472, -456, -504, -488, -408, -392, -440, -424,
-88, -72, -120, -104, -24, -8, -56, -40,
-216, -200, -248, -232, -152, -136, -184, -168,
-1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
-1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
-688, -656, -752, -720, -560, -528, -624, -592,
-944, -912, -1008, -976, -816, -784, -880, -848,
5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
344, 328, 376, 360, 280, 264, 312, 296,
472, 456, 504, 488, 408, 392, 440, 424,
88, 72, 120, 104, 24, 8, 56, 40,
216, 200, 248, 232, 152, 136, 184, 168,
1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
688, 656, 752, 720, 560, 528, 624, 592,
944, 912, 1008, 976, 816, 784, 880, 848
}};
} // namespace al

View file

@ -9,8 +9,75 @@
namespace al {
extern const std::array<std::int16_t,256> muLawDecompressionTable;
extern const std::array<std::int16_t,256> aLawDecompressionTable;
inline constexpr auto muLawDecompressionTable = std::array<int16_t,256>{{
-32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
-23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
-15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
-11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
-7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
-5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
-3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
-2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
-1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
-1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
-876, -844, -812, -780, -748, -716, -684, -652,
-620, -588, -556, -524, -492, -460, -428, -396,
-372, -356, -340, -324, -308, -292, -276, -260,
-244, -228, -212, -196, -180, -164, -148, -132,
-120, -112, -104, -96, -88, -80, -72, -64,
-56, -48, -40, -32, -24, -16, -8, 0,
32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
876, 844, 812, 780, 748, 716, 684, 652,
620, 588, 556, 524, 492, 460, 428, 396,
372, 356, 340, 324, 308, 292, 276, 260,
244, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64,
56, 48, 40, 32, 24, 16, 8, 0
}};
inline constexpr auto aLawDecompressionTable = std::array<int16_t,256>{{
-5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
-7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
-2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
-3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
-22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
-30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
-11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472,
-15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
-344, -328, -376, -360, -280, -264, -312, -296,
-472, -456, -504, -488, -408, -392, -440, -424,
-88, -72, -120, -104, -24, -8, -56, -40,
-216, -200, -248, -232, -152, -136, -184, -168,
-1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
-1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
-688, -656, -752, -720, -560, -528, -624, -592,
-944, -912, -1008, -976, -816, -784, -880, -848,
5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
344, 328, 376, 360, 280, 264, 312, 296,
472, 456, 504, 488, 408, 392, 440, 424,
88, 72, 120, 104, 24, 8, 56, 40,
216, 200, 248, 232, 152, 136, 184, 168,
1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
688, 656, 752, 720, 560, 528, 624, 592,
944, 912, 1008, 976, 816, 784, 880, 848
}};
template<FmtType T>

View file

@ -1,24 +1,27 @@
#include "config.h"
#include "config_simd.h"
#include "fpu_ctrl.h"
#ifdef HAVE_INTRIN_H
#include <intrin.h>
#endif
#ifdef HAVE_SSE_INTRINSICS
#if HAVE_SSE_INTRINSICS
#include <emmintrin.h>
#elif defined(HAVE_SSE)
#elif HAVE_SSE
#include <xmmintrin.h>
#endif
#if defined(HAVE_SSE) && !defined(_MM_DENORMALS_ZERO_MASK)
#if HAVE_SSE && !defined(_MM_DENORMALS_ZERO_MASK)
/* Some headers seem to be missing these? */
#define _MM_DENORMALS_ZERO_MASK 0x0040u
#define _MM_DENORMALS_ZERO_ON 0x0040u
#endif
#if !HAVE_SSE_INTRINSICS && HAVE_SSE
#include "cpu_caps.h"
#endif
namespace {
@ -28,14 +31,14 @@ namespace {
[[maybe_unused]]
void disable_denormals(unsigned int *state [[maybe_unused]])
{
#if defined(HAVE_SSE_INTRINSICS)
#if HAVE_SSE_INTRINSICS
*state = _mm_getcsr();
unsigned int sseState{*state};
sseState &= ~(_MM_FLUSH_ZERO_MASK | _MM_DENORMALS_ZERO_MASK);
sseState |= _MM_FLUSH_ZERO_ON | _MM_DENORMALS_ZERO_ON;
_mm_setcsr(sseState);
#elif defined(HAVE_SSE)
#elif HAVE_SSE
*state = _mm_getcsr();
unsigned int sseState{*state};
@ -56,7 +59,7 @@ void disable_denormals(unsigned int *state [[maybe_unused]])
[[maybe_unused]]
void reset_fpu(unsigned int state [[maybe_unused]])
{
#if defined(HAVE_SSE_INTRINSICS) || defined(HAVE_SSE)
#if HAVE_SSE_INTRINSICS || HAVE_SSE
_mm_setcsr(state);
#endif
}
@ -67,9 +70,9 @@ void reset_fpu(unsigned int state [[maybe_unused]])
unsigned int FPUCtl::Set() noexcept
{
unsigned int state{};
#if defined(HAVE_SSE_INTRINSICS)
#if HAVE_SSE_INTRINSICS
disable_denormals(&state);
#elif defined(HAVE_SSE)
#elif HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
disable_denormals(&state);
#endif
@ -78,9 +81,9 @@ unsigned int FPUCtl::Set() noexcept
void FPUCtl::Reset(unsigned int state [[maybe_unused]]) noexcept
{
#if defined(HAVE_SSE_INTRINSICS)
#if HAVE_SSE_INTRINSICS
reset_fpu(state);
#elif defined(HAVE_SSE)
#elif HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
reset_fpu(state);
#endif

View file

@ -11,7 +11,7 @@
struct FrontStablizer {
FrontStablizer(size_t numchans) : ChannelFilters{numchans} { }
explicit FrontStablizer(size_t numchans) : ChannelFilters{numchans} { }
alignas(16) std::array<float,BufferLineSize> MidDirect{};
alignas(16) std::array<float,BufferLineSize> Side{};

View file

@ -11,17 +11,18 @@
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <limits>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <system_error>
#include "almalloc.h"
#include "alnumeric.h"
#include "alspan.h"
#include "alstring.h"
#include "filesystem.h"
#include "logging.h"
#include "strutils.h"
@ -32,11 +33,9 @@ using namespace std::string_view_literals;
std::mutex gSearchLock;
void DirectorySearch(const std::filesystem::path &path, const std::string_view ext,
void DirectorySearch(const fs::path &path, const std::string_view ext,
std::vector<std::string> *const results)
{
namespace fs = std::filesystem;
const auto base = results->size();
try {
@ -44,26 +43,28 @@ void DirectorySearch(const std::filesystem::path &path, const std::string_view e
if(!fs::exists(fpath))
return;
TRACE("Searching %s for *%.*s\n", fpath.u8string().c_str(), al::sizei(ext), ext.data());
TRACE("Searching {} for *{}", al::u8_as_char(fpath.u8string()), ext);
for(auto&& dirent : fs::directory_iterator{fpath})
{
auto&& entrypath = dirent.path();
if(!entrypath.has_extension())
continue;
if(fs::status(entrypath).type() == fs::file_type::regular
&& al::case_compare(entrypath.extension().u8string(), ext) == 0)
results->emplace_back(entrypath.u8string());
if(fs::status(entrypath).type() != fs::file_type::regular)
continue;
const auto u8ext = entrypath.extension().u8string();
if(al::case_compare(al::u8_as_char(u8ext), ext) == 0)
results->emplace_back(al::u8_as_char(entrypath.u8string()));
}
}
catch(std::exception& e) {
ERR("Exception enumerating files: %s\n", e.what());
ERR("Exception enumerating files: {}", e.what());
}
const auto newlist = al::span{*results}.subspan(base);
std::sort(newlist.begin(), newlist.end());
for(const auto &name : newlist)
TRACE(" got %s\n", name.c_str());
TRACE(" got {}", name);
}
} // namespace
@ -77,7 +78,7 @@ const PathNamePair &GetProcBinary()
{
auto get_procbin = []
{
#if !defined(ALSOFT_UWP)
#if !ALSOFT_UWP
DWORD pathlen{256};
auto fullpath = std::wstring(pathlen, L'\0');
DWORD len{GetModuleFileNameW(nullptr, fullpath.data(), pathlen)};
@ -95,16 +96,22 @@ const PathNamePair &GetProcBinary()
}
if(len == 0)
{
ERR("Failed to get process name: error %lu\n", GetLastError());
ERR("Failed to get process name: error {}", GetLastError());
return PathNamePair{};
}
fullpath.resize(len);
#else
if(__argc < 1 || !__wargv)
{
ERR("Failed to get process name: __argc = {}, __wargv = {}", __argc,
static_cast<void*>(__wargv));
return PathNamePair{};
}
const WCHAR *exePath{__wargv[0]};
if(!exePath)
{
ERR("Failed to get process name: __wargv[0] == nullptr\n");
ERR("Failed to get process name: __wargv[0] == nullptr");
return PathNamePair{};
}
std::wstring fullpath{exePath};
@ -120,7 +127,7 @@ const PathNamePair &GetProcBinary()
else
res.fname = wstr_to_utf8(fullpath);
TRACE("Got binary: %s, %s\n", res.path.c_str(), res.fname.c_str());
TRACE("Got binary: {}, {}", res.path, res.fname);
return res;
};
static const PathNamePair procbin{get_procbin()};
@ -129,7 +136,7 @@ const PathNamePair &GetProcBinary()
namespace {
#if !defined(ALSOFT_UWP) && !defined(_GAMING_XBOX)
#if !ALSOFT_UWP && !defined(_GAMING_XBOX)
struct CoTaskMemDeleter {
void operator()(void *mem) const { CoTaskMemFree(mem); }
};
@ -137,26 +144,35 @@ struct CoTaskMemDeleter {
} // namespace
std::vector<std::string> SearchDataFiles(const std::string_view ext, const std::string_view subdir)
auto SearchDataFiles(const std::string_view ext) -> std::vector<std::string>
{
auto srchlock = std::lock_guard{gSearchLock};
/* Search the app-local directory. */
auto results = std::vector<std::string>{};
if(auto localpath = al::getenv(L"ALSOFT_LOCAL_PATH"))
DirectorySearch(*localpath, ext, &results);
else if(auto curpath = fs::current_path(); !curpath.empty())
DirectorySearch(curpath, ext, &results);
return results;
}
auto SearchDataFiles(const std::string_view ext, const std::string_view subdir)
-> std::vector<std::string>
{
std::lock_guard<std::mutex> srchlock{gSearchLock};
/* If the path is absolute, use it directly. */
std::vector<std::string> results;
auto path = std::filesystem::u8path(subdir);
auto path = fs::u8path(subdir);
if(path.is_absolute())
{
DirectorySearch(path, ext, &results);
return results;
}
/* Search the app-local directory. */
if(auto localpath = al::getenv(L"ALSOFT_LOCAL_PATH"))
DirectorySearch(*localpath, ext, &results);
else if(auto curpath = std::filesystem::current_path(); !curpath.empty())
DirectorySearch(curpath, ext, &results);
#if !defined(ALSOFT_UWP) && !defined(_GAMING_XBOX)
#if !ALSOFT_UWP && !defined(_GAMING_XBOX)
/* Search the local and global data dirs. */
for(const auto &folderid : std::array{FOLDERID_RoamingAppData, FOLDERID_ProgramData})
{
@ -166,7 +182,7 @@ std::vector<std::string> SearchDataFiles(const std::string_view ext, const std::
if(FAILED(hr) || !buffer || !*buffer)
continue;
DirectorySearch(std::filesystem::path{buffer.get()}/path, ext, &results);
DirectorySearch(fs::path{buffer.get()}/path, ext, &results);
}
#endif
@ -175,11 +191,11 @@ std::vector<std::string> SearchDataFiles(const std::string_view ext, const std::
void SetRTPriority()
{
#if !defined(ALSOFT_UWP)
#if !ALSOFT_UWP
if(RTPrioLevel > 0)
{
if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
ERR("Failed to set priority level for thread\n");
ERR("Failed to set priority level for thread");
}
#endif
}
@ -202,7 +218,7 @@ void SetRTPriority()
#include <pthread.h>
#include <sched.h>
#endif
#ifdef HAVE_RTKIT
#if HAVE_RTKIT
#include <sys/resource.h>
#include "dbus_wrap.h"
@ -221,8 +237,8 @@ const PathNamePair &GetProcBinary()
size_t pathlen{};
std::array<int,4> mib{{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}};
if(sysctl(mib.data(), mib.size(), nullptr, &pathlen, nullptr, 0) == -1)
WARN("Failed to sysctl kern.proc.pathname: %s\n",
std::generic_category().message(errno).c_str());
WARN("Failed to sysctl kern.proc.pathname: {}",
std::generic_category().message(errno));
else
{
auto procpath = std::vector<char>(pathlen+1, '\0');
@ -236,8 +252,8 @@ const PathNamePair &GetProcBinary()
std::array<char,PROC_PIDPATHINFO_MAXSIZE> procpath{};
const pid_t pid{getpid()};
if(proc_pidpath(pid, procpath.data(), procpath.size()) < 1)
ERR("proc_pidpath(%d, ...) failed: %s\n", pid,
std::generic_category().message(errno).c_str());
ERR("proc_pidpath({}, ...) failed: {}", pid,
std::generic_category().message(errno));
else
pathname = procpath.data();
}
@ -263,17 +279,16 @@ const PathNamePair &GetProcBinary()
for(const std::string_view name : SelfLinkNames)
{
try {
if(!std::filesystem::exists(name))
if(!fs::exists(name))
continue;
if(auto path = std::filesystem::read_symlink(name); !path.empty())
if(auto path = fs::read_symlink(name); !path.empty())
{
pathname = path.u8string();
pathname = al::u8_as_char(path.u8string());
break;
}
}
catch(std::exception& e) {
WARN("Exception getting symlink %.*s: %s\n", al::sizei(name), name.data(),
e.what());
WARN("Exception getting symlink {}: {}", name, e.what());
}
}
}
@ -288,36 +303,45 @@ const PathNamePair &GetProcBinary()
else
res.fname = pathname;
TRACE("Got binary: \"%s\", \"%s\"\n", res.path.c_str(), res.fname.c_str());
TRACE("Got binary: \"{}\", \"{}\"", res.path, res.fname);
return res;
};
static const PathNamePair procbin{get_procbin()};
return procbin;
}
std::vector<std::string> SearchDataFiles(const std::string_view ext, const std::string_view subdir)
auto SearchDataFiles(const std::string_view ext) -> std::vector<std::string>
{
auto srchlock = std::lock_guard{gSearchLock};
/* Search the app-local directory. */
auto results = std::vector<std::string>{};
if(auto localpath = al::getenv("ALSOFT_LOCAL_PATH"))
DirectorySearch(*localpath, ext, &results);
else if(auto curpath = fs::current_path(); !curpath.empty())
DirectorySearch(curpath, ext, &results);
return results;
}
auto SearchDataFiles(const std::string_view ext, const std::string_view subdir)
-> std::vector<std::string>
{
std::lock_guard<std::mutex> srchlock{gSearchLock};
std::vector<std::string> results;
auto path = std::filesystem::u8path(subdir);
auto path = fs::u8path(subdir);
if(path.is_absolute())
{
DirectorySearch(path, ext, &results);
return results;
}
/* Search the app-local directory. */
if(auto localpath = al::getenv("ALSOFT_LOCAL_PATH"))
DirectorySearch(*localpath, ext, &results);
else if(auto curpath = std::filesystem::current_path(); !curpath.empty())
DirectorySearch(curpath, ext, &results);
/* Search local data dir */
if(auto datapath = al::getenv("XDG_DATA_HOME"))
DirectorySearch(std::filesystem::path{*datapath}/path, ext, &results);
DirectorySearch(fs::path{*datapath}/path, ext, &results);
else if(auto homepath = al::getenv("HOME"))
DirectorySearch(std::filesystem::path{*homepath}/".local/share"/path, ext, &results);
DirectorySearch(fs::path{*homepath}/".local/share"/path, ext, &results);
/* Search global data dirs */
std::string datadirs{al::getenv("XDG_DATA_DIRS").value_or("/usr/local/share/:/usr/share/")};
@ -333,12 +357,12 @@ std::vector<std::string> SearchDataFiles(const std::string_view ext, const std::
curpos = nextpos;
if(!pathname.empty())
DirectorySearch(std::filesystem::path{pathname}/path, ext, &results);
DirectorySearch(fs::path{pathname}/path, ext, &results);
}
#ifdef ALSOFT_INSTALL_DATADIR
/* Search the installation data directory */
if(auto instpath = std::filesystem::path{ALSOFT_INSTALL_DATADIR}; !instpath.empty())
if(auto instpath = fs::path{ALSOFT_INSTALL_DATADIR}; !instpath.empty())
DirectorySearch(instpath/path, ext, &results);
#endif
@ -368,24 +392,23 @@ bool SetRTPriorityPthread(int prio [[maybe_unused]])
err = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
if(err == 0) return true;
#endif
WARN("pthread_setschedparam failed: %s (%d)\n", std::generic_category().message(err).c_str(),
err);
WARN("pthread_setschedparam failed: {} ({})", std::generic_category().message(err), err);
return false;
}
bool SetRTPriorityRTKit(int prio [[maybe_unused]])
{
#ifdef HAVE_RTKIT
#if HAVE_RTKIT
if(!HasDBus())
{
WARN("D-Bus not available\n");
WARN("D-Bus not available");
return false;
}
dbus::Error error;
dbus::ConnectionPtr conn{dbus_bus_get(DBUS_BUS_SYSTEM, &error.get())};
if(!conn)
{
WARN("D-Bus connection failed with %s: %s\n", error->name, error->message);
WARN("D-Bus connection failed with {}: {}", error->name, error->message);
return false;
}
@ -397,11 +420,11 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]])
if(err == -ENOENT)
{
err = std::abs(err);
ERR("Could not query RTKit: %s (%d)\n", std::generic_category().message(err).c_str(), err);
ERR("Could not query RTKit: {} ({})", std::generic_category().message(err), err);
return false;
}
int rtmax{rtkit_get_max_realtime_priority(conn.get())};
TRACE("Maximum real-time priority: %d, minimum niceness: %d\n", rtmax, nicemin);
TRACE("Maximum real-time priority: {}, minimum niceness: {}", rtmax, nicemin);
auto limit_rttime = [](DBusConnection *c) -> int
{
@ -414,8 +437,7 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]])
if(getrlimit(RLIMIT_RTTIME, &rlim) != 0)
return errno;
TRACE("RTTime max: %llu (hard: %llu, soft: %llu)\n", umaxtime,
static_cast<ulonglong>(rlim.rlim_max), static_cast<ulonglong>(rlim.rlim_cur));
TRACE("RTTime max: {} (hard: {}, soft: {})", umaxtime, rlim.rlim_max, rlim.rlim_cur);
if(rlim.rlim_max > umaxtime)
{
rlim.rlim_max = static_cast<rlim_t>(std::min<ulonglong>(umaxtime,
@ -432,21 +454,21 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]])
{
err = limit_rttime(conn.get());
if(err != 0)
WARN("Failed to set RLIMIT_RTTIME for RTKit: %s (%d)\n",
std::generic_category().message(err).c_str(), err);
WARN("Failed to set RLIMIT_RTTIME for RTKit: {} ({})",
std::generic_category().message(err), err);
}
/* Limit the maximum real-time priority to half. */
rtmax = (rtmax+1)/2;
prio = std::clamp(prio, 1, rtmax);
TRACE("Making real-time with priority %d (max: %d)\n", prio, rtmax);
TRACE("Making real-time with priority {} (max: {})", prio, rtmax);
err = rtkit_make_realtime(conn.get(), 0, prio);
if(err == 0) return true;
err = std::abs(err);
WARN("Failed to set real-time priority: %s (%d)\n",
std::generic_category().message(err).c_str(), err);
WARN("Failed to set real-time priority: {} ({})",
std::generic_category().message(err), err);
}
/* Don't try to set the niceness for non-Linux systems. Standard POSIX has
* niceness as a per-process attribute, while the intent here is for the
@ -456,19 +478,18 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]])
#ifdef __linux__
if(nicemin < 0)
{
TRACE("Making high priority with niceness %d\n", nicemin);
TRACE("Making high priority with niceness {}", nicemin);
err = rtkit_make_high_priority(conn.get(), 0, nicemin);
if(err == 0) return true;
err = std::abs(err);
WARN("Failed to set high priority: %s (%d)\n",
std::generic_category().message(err).c_str(), err);
WARN("Failed to set high priority: {} ({})", std::generic_category().message(err), err);
}
#endif /* __linux__ */
#else
WARN("D-Bus not supported\n");
WARN("D-Bus not supported");
#endif
return false;
}

View file

@ -19,6 +19,8 @@ inline bool AllowRTTimeLimit{true};
void SetRTPriority();
std::vector<std::string> SearchDataFiles(const std::string_view ext, const std::string_view subdir);
auto SearchDataFiles(const std::string_view ext) -> std::vector<std::string>;
auto SearchDataFiles(const std::string_view ext, const std::string_view subdir)
-> std::vector<std::string>;
#endif /* CORE_HELPERS_H */

View file

@ -12,7 +12,6 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <memory>
@ -31,7 +30,9 @@
#include "alspan.h"
#include "alstring.h"
#include "ambidefs.h"
#include "filesystem.h"
#include "filters/splitter.h"
#include "fmt/core.h"
#include "helpers.h"
#include "logging.h"
#include "mixer/hrtfdefs.h"
@ -103,11 +104,16 @@ constexpr uint MaxSampleRate{0xff'ff'ff};
static_assert(MaxHrirDelay*HrirDelayFracOne < 256, "MAX_HRIR_DELAY or DELAY_FRAC too large");
constexpr auto HeaderMarkerSize = 8_uz;
[[nodiscard]] constexpr auto GetMarker00Name() noexcept { return "MinPHR00"sv; }
[[nodiscard]] constexpr auto GetMarker01Name() noexcept { return "MinPHR01"sv; }
[[nodiscard]] constexpr auto GetMarker02Name() noexcept { return "MinPHR02"sv; }
[[nodiscard]] constexpr auto GetMarker03Name() noexcept { return "MinPHR03"sv; }
static_assert(GetMarker00Name().size() == HeaderMarkerSize);
static_assert(GetMarker01Name().size() == HeaderMarkerSize);
static_assert(GetMarker02Name().size() == HeaderMarkerSize);
static_assert(GetMarker03Name().size() == HeaderMarkerSize);
/* First value for pass-through coefficients (remaining are 0), used for omni-
* directional sounds. */
@ -176,7 +182,7 @@ class databuf final : public std::streambuf {
}
public:
databuf(const al::span<char_type> data) noexcept
explicit databuf(const al::span<char_type> data) noexcept
{
setg(data.data(), data.data(), al::to_address(data.end()));
}
@ -187,7 +193,7 @@ class idstream final : public std::istream {
databuf mStreamBuf;
public:
idstream(const al::span<char_type> data) : std::istream{nullptr}, mStreamBuf{data}
explicit idstream(const al::span<char_type> data) : std::istream{nullptr}, mStreamBuf{data}
{ init(&mStreamBuf); }
};
@ -198,10 +204,9 @@ struct IdxBlend { uint idx; float blend; };
*/
IdxBlend CalcEvIndex(uint evcount, float ev)
{
ev = (al::numbers::pi_v<float>*0.5f + ev) * static_cast<float>(evcount-1) *
al::numbers::inv_pi_v<float>;
uint idx{float2uint(ev)};
ev = (al::numbers::inv_pi_v<float>*ev + 0.5f) * static_cast<float>(evcount-1);
const auto idx = float2uint(ev);
return IdxBlend{std::min(idx, evcount-1u), ev-static_cast<float>(idx)};
}
@ -210,10 +215,9 @@ IdxBlend CalcEvIndex(uint evcount, float ev)
*/
IdxBlend CalcAzIndex(uint azcount, float az)
{
az = (al::numbers::pi_v<float>*2.0f + az) * static_cast<float>(azcount) *
(al::numbers::inv_pi_v<float>*0.5f);
uint idx{float2uint(az)};
az = (al::numbers::inv_pi_v<float>*0.5f*az + 1.0f) * static_cast<float>(azcount);
const auto idx = float2uint(az);
return IdxBlend{idx%azcount, az-static_cast<float>(idx)};
}
@ -348,7 +352,7 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool
auto hrir_delay_round = [](const uint d) noexcept -> uint
{ return (d+HrirDelayFracHalf) >> HrirDelayFracBits; };
TRACE("Min delay: %.2f, max delay: %.2f, FIR length: %u\n",
TRACE("Min delay: {:.2f}, max delay: {:.2f}, FIR length: {}",
min_delay/double{HrirDelayFracOne}, max_delay/double{HrirDelayFracOne}, irSize);
auto tmpres = std::vector<std::array<double2,HrirLength>>(mChannels.size());
@ -389,7 +393,7 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool
tmpres.clear();
const uint max_length{std::min(hrir_delay_round(max_delay) + irSize, HrirLength)};
TRACE("New max delay: %.2f, FIR length: %u\n", max_delay/double{HrirDelayFracOne},
TRACE("New max delay: {:.2f}, FIR length: {}", max_delay/double{HrirDelayFracOne},
max_length);
mIrSize = max_length;
}
@ -544,13 +548,13 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data)
if(irSize < MinIrLength || irSize > HrirLength)
{
ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
ERR("Unsupported HRIR size, irSize={} ({} to {})", irSize, MinIrLength, HrirLength);
return nullptr;
}
if(evCount < MinEvCount || evCount > MaxEvCount)
{
ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
evCount, MinEvCount, MaxEvCount);
ERR("Unsupported elevation count: evCount={} ({} to {})", evCount, MinEvCount,
MaxEvCount);
return nullptr;
}
@ -564,15 +568,15 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data)
{
if(elevs[i].irOffset <= elevs[i-1].irOffset)
{
ERR("Invalid evOffset: evOffset[%zu]=%d (last=%d)\n", i, elevs[i].irOffset,
ERR("Invalid evOffset: evOffset[{}]={} (last={})", i, elevs[i].irOffset,
elevs[i-1].irOffset);
return nullptr;
}
}
if(irCount <= elevs.back().irOffset)
{
ERR("Invalid evOffset: evOffset[%zu]=%d (irCount=%d)\n",
elevs.size()-1, elevs.back().irOffset, irCount);
ERR("Invalid evOffset: evOffset[{}]={} (irCount={})", elevs.size()-1,
elevs.back().irOffset, irCount);
return nullptr;
}
@ -581,16 +585,16 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data)
elevs[i-1].azCount = static_cast<ushort>(elevs[i].irOffset - elevs[i-1].irOffset);
if(elevs[i-1].azCount < MinAzCount || elevs[i-1].azCount > MaxAzCount)
{
ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n",
i-1, elevs[i-1].azCount, MinAzCount, MaxAzCount);
ERR("Unsupported azimuth count: azCount[{}]={} ({} to {})", i-1, elevs[i-1].azCount,
MinAzCount, MaxAzCount);
return nullptr;
}
}
elevs.back().azCount = static_cast<ushort>(irCount - elevs.back().irOffset);
if(elevs.back().azCount < MinAzCount || elevs.back().azCount > MaxAzCount)
{
ERR("Unsupported azimuth count: azCount[%zu]=%d (%d to %d)\n",
elevs.size()-1, elevs.back().azCount, MinAzCount, MaxAzCount);
ERR("Unsupported azimuth count: azCount[{}]={} ({} to {})", elevs.size()-1,
elevs.back().azCount, MinAzCount, MaxAzCount);
return nullptr;
}
@ -610,7 +614,7 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data)
{
if(delays[i][0] > MaxHrirDelay)
{
ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
ERR("Invalid delays[{}]: {} ({})", i, delays[i][0], MaxHrirDelay);
return nullptr;
}
delays[i][0] <<= HrirDelayFracBits;
@ -634,13 +638,13 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data)
if(irSize < MinIrLength || irSize > HrirLength)
{
ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
ERR("Unsupported HRIR size, irSize={} ({} to {})", irSize, MinIrLength, HrirLength);
return nullptr;
}
if(evCount < MinEvCount || evCount > MaxEvCount)
{
ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
evCount, MinEvCount, MaxEvCount);
ERR("Unsupported elevation count: evCount={} ({} to {})", evCount, MinEvCount,
MaxEvCount);
return nullptr;
}
@ -654,7 +658,7 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data)
{
if(elevs[i].azCount < MinAzCount || elevs[i].azCount > MaxAzCount)
{
ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n", i, elevs[i].azCount,
ERR("Unsupported azimuth count: azCount[{}]={} ({} to {})", i, elevs[i].azCount,
MinAzCount, MaxAzCount);
return nullptr;
}
@ -681,7 +685,7 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data)
{
if(delays[i][0] > MaxHrirDelay)
{
ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
ERR("Invalid delays[{}]: {} ({})", i, delays[i][0], MaxHrirDelay);
return nullptr;
}
delays[i][0] <<= HrirDelayFracBits;
@ -711,23 +715,23 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)
if(sampleType > SampleType_S24)
{
ERR("Unsupported sample type: %d\n", sampleType);
ERR("Unsupported sample type: {}", sampleType);
return nullptr;
}
if(channelType > ChanType_LeftRight)
{
ERR("Unsupported channel type: %d\n", channelType);
ERR("Unsupported channel type: {}", channelType);
return nullptr;
}
if(irSize < MinIrLength || irSize > HrirLength)
{
ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
ERR("Unsupported HRIR size, irSize={} ({} to {})", irSize, MinIrLength, HrirLength);
return nullptr;
}
if(fdCount < 1 || fdCount > MaxFdCount)
{
ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount,
ERR("Unsupported number of field-depths: fdCount={} ({} to {})", fdCount, MinFdCount,
MaxFdCount);
return nullptr;
}
@ -743,13 +747,13 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)
if(distance < MinFdDistance || distance > MaxFdDistance)
{
ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance,
ERR("Unsupported field distance[{}]={} ({} to {} millimeters)", f, distance,
MinFdDistance, MaxFdDistance);
return nullptr;
}
if(evCount < MinEvCount || evCount > MaxEvCount)
{
ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount,
ERR("Unsupported elevation count: evCount[{}]={} ({} to {})", f, evCount,
MinEvCount, MaxEvCount);
return nullptr;
}
@ -758,7 +762,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)
fields[f].evCount = evCount;
if(f > 0 && fields[f].distance <= fields[f-1].distance)
{
ERR("Field distance[%zu] is not after previous (%f > %f)\n", f, fields[f].distance,
ERR("Field distance[{}] is not after previous ({:f} > {:f})", f, fields[f].distance,
fields[f-1].distance);
return nullptr;
}
@ -774,7 +778,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)
{
if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount)
{
ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e,
ERR("Unsupported azimuth count: azCount[{}][{}]={} ({} to {})", f, e,
elevs[ebase+e].azCount, MinAzCount, MaxAzCount);
return nullptr;
}
@ -820,7 +824,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)
{
if(delays[i][0] > MaxHrirDelay)
{
ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
ERR("Invalid delays[{}][0]: {} ({})", i, delays[i][0], MaxHrirDelay);
return nullptr;
}
delays[i][0] <<= HrirDelayFracBits;
@ -865,12 +869,12 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)
{
if(delays[i][0] > MaxHrirDelay)
{
ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
ERR("Invalid delays[{}][0]: {} ({})", i, delays[i][0], MaxHrirDelay);
return nullptr;
}
if(delays[i][1] > MaxHrirDelay)
{
ERR("Invalid delays[%zu][1]: %d (%d)\n", i, delays[i][1], MaxHrirDelay);
ERR("Invalid delays[{}][1]: {} ({})", i, delays[i][1], MaxHrirDelay);
return nullptr;
}
delays[i][0] <<= HrirDelayFracBits;
@ -963,18 +967,18 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)
if(channelType > ChanType_LeftRight)
{
ERR("Unsupported channel type: %d\n", channelType);
ERR("Unsupported channel type: {}", channelType);
return nullptr;
}
if(irSize < MinIrLength || irSize > HrirLength)
{
ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
ERR("Unsupported HRIR size, irSize={} ({} to {})", irSize, MinIrLength, HrirLength);
return nullptr;
}
if(fdCount < 1 || fdCount > MaxFdCount)
{
ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount,
ERR("Unsupported number of field-depths: fdCount={} ({} to {})", fdCount, MinFdCount,
MaxFdCount);
return nullptr;
}
@ -990,13 +994,13 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)
if(distance < MinFdDistance || distance > MaxFdDistance)
{
ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance,
ERR("Unsupported field distance[{}]={} ({} to {} millimeters)", f, distance,
MinFdDistance, MaxFdDistance);
return nullptr;
}
if(evCount < MinEvCount || evCount > MaxEvCount)
{
ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount,
ERR("Unsupported elevation count: evCount[{}]={} ({} to {})", f, evCount,
MinEvCount, MaxEvCount);
return nullptr;
}
@ -1005,8 +1009,8 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)
fields[f].evCount = evCount;
if(f > 0 && fields[f].distance > fields[f-1].distance)
{
ERR("Field distance[%zu] is not before previous (%f <= %f)\n", f, fields[f].distance,
fields[f-1].distance);
ERR("Field distance[{}] is not before previous ({:f} <= {:f})", f,
fields[f].distance, fields[f-1].distance);
return nullptr;
}
@ -1021,7 +1025,7 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)
{
if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount)
{
ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e,
ERR("Unsupported azimuth count: azCount[{}][{}]={} ({} to {})", f, e,
elevs[ebase+e].azCount, MinAzCount, MaxAzCount);
return nullptr;
}
@ -1056,8 +1060,8 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)
{
if(delays[i][0] > MaxHrirDelay<<HrirDelayFracBits)
{
ERR("Invalid delays[%zu][0]: %f (%d)\n", i,
delays[i][0] / float{HrirDelayFracOne}, MaxHrirDelay);
ERR("Invalid delays[{}][0]: {:f} ({})", i, delays[i][0]/float{HrirDelayFracOne},
MaxHrirDelay);
return nullptr;
}
}
@ -1087,14 +1091,14 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)
{
if(delays[i][0] > MaxHrirDelay<<HrirDelayFracBits)
{
ERR("Invalid delays[%zu][0]: %f (%d)\n", i,
delays[i][0] / float{HrirDelayFracOne}, MaxHrirDelay);
ERR("Invalid delays[{}][0]: {:f} ({})", i, delays[i][0]/float{HrirDelayFracOne},
MaxHrirDelay);
return nullptr;
}
if(delays[i][1] > MaxHrirDelay<<HrirDelayFracBits)
{
ERR("Invalid delays[%zu][1]: %f (%d)\n", i,
delays[i][1] / float{HrirDelayFracOne}, MaxHrirDelay);
ERR("Invalid delays[{}][1]: {:f} ({})", i, delays[i][1]/float{HrirDelayFracOne},
MaxHrirDelay);
return nullptr;
}
}
@ -1115,35 +1119,29 @@ void AddFileEntry(const std::string_view filename)
{
/* Check if this file has already been enumerated. */
auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(),
[filename](const HrtfEntry &entry) -> bool
{ return entry.mFilename == filename; });
[filename](const HrtfEntry &entry) -> bool { return entry.mFilename == filename; });
if(enum_iter != EnumeratedHrtfs.cend())
{
TRACE("Skipping duplicate file entry %.*s\n", al::sizei(filename), filename.data());
TRACE("Skipping duplicate file entry {}", filename);
return;
}
/* TODO: Get a human-readable name from the HRTF data (possibly coming in a
* format update). */
size_t namepos{filename.rfind('/')+1};
if(!namepos) namepos = filename.rfind('\\')+1;
* format update).
*/
const auto namepos = std::max(filename.rfind('/')+1, filename.rfind('\\')+1);
const auto extpos = filename.substr(namepos).rfind('.');
size_t extpos{filename.rfind('.')};
if(extpos <= namepos) extpos = std::string::npos;
const auto basename = (extpos == std::string::npos) ?
filename.substr(namepos) : filename.substr(namepos, extpos);
const std::string_view basename{(extpos == std::string::npos) ?
filename.substr(namepos) : filename.substr(namepos, extpos-namepos)};
std::string newname{basename};
int count{1};
auto count = 1;
auto newname = std::string{basename};
while(checkName(newname))
{
newname = basename;
newname += " #";
newname += std::to_string(++count);
}
const HrtfEntry &entry = EnumeratedHrtfs.emplace_back(newname, filename);
newname = fmt::format("{} #{}", basename, ++count);
TRACE("Adding file entry \"%s\"\n", entry.mFilename.c_str());
const auto &entry = EnumeratedHrtfs.emplace_back(newname, filename);
TRACE("Adding file entry \"{}\"", entry.mFilename);
}
/* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer
@ -1151,32 +1149,26 @@ void AddFileEntry(const std::string_view filename)
*/
void AddBuiltInEntry(const std::string_view dispname, uint residx)
{
std::string filename{'!'+std::to_string(residx)+'_'};
filename += dispname;
auto filename = fmt::format("!{}_{}", residx, dispname);
auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(),
[&filename](const HrtfEntry &entry) -> bool
{ return entry.mFilename == filename; });
[&filename](const HrtfEntry &entry) -> bool { return entry.mFilename == filename; });
if(enum_iter != EnumeratedHrtfs.cend())
{
TRACE("Skipping duplicate file entry %s\n", filename.c_str());
TRACE("Skipping duplicate file entry {}", filename);
return;
}
/* TODO: Get a human-readable name from the HRTF data (possibly coming in a
* format update). */
std::string newname{dispname};
int count{1};
auto count = 1;
auto newname = std::string{dispname};
while(checkName(newname))
{
newname = dispname;
newname += " #";
newname += std::to_string(++count);
}
const HrtfEntry &entry = EnumeratedHrtfs.emplace_back(std::move(newname), std::move(filename));
newname = fmt::format("{} #{}", dispname, ++count);
TRACE("Adding built-in entry \"%s\"\n", entry.mFilename.c_str());
const auto &entry = EnumeratedHrtfs.emplace_back(std::move(newname), std::move(filename));
TRACE("Adding built-in entry \"{}\"", entry.mFilename);
}
@ -1210,6 +1202,9 @@ std::vector<std::string> EnumerateHrtf(std::optional<std::string> pathopt)
std::lock_guard<std::mutex> enumlock{EnumeratedHrtfLock};
EnumeratedHrtfs.clear();
for(const auto &fname : SearchDataFiles(".mhr"sv))
AddFileEntry(fname);
bool usedefaults{true};
if(pathopt)
{
@ -1262,7 +1257,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string_view name, const uint devrate)
try {
if(devrate > MaxSampleRate)
{
WARN("Device sample rate too large for HRTF (%uhz > %uhz)\n", devrate, MaxSampleRate);
WARN("Device sample rate too large for HRTF ({}hz > {}hz)", devrate, MaxSampleRate);
return nullptr;
}
std::lock_guard<std::mutex> enumlock{EnumeratedHrtfLock};
@ -1292,13 +1287,14 @@ try {
std::unique_ptr<std::istream> stream;
int residx{};
char ch{};
/* NOLINTNEXTLINE(cert-err34-c,cppcoreguidelines-pro-type-vararg) */
if(sscanf(fname.c_str(), "!%d%c", &residx, &ch) == 2 && ch == '_')
{
TRACE("Loading %s...\n", fname.c_str());
TRACE("Loading {}...", fname);
al::span<const char> res{GetResource(residx)};
if(res.empty())
{
ERR("Could not get resource %u, %.*s\n", residx, al::sizei(name), name.data());
ERR("Could not get resource {}, {}", residx, name);
return nullptr;
}
/* NOLINTNEXTLINE(*-const-cast) */
@ -1306,44 +1302,44 @@ try {
}
else
{
TRACE("Loading %s...\n", fname.c_str());
auto fstr = std::make_unique<std::ifstream>(std::filesystem::u8path(fname),
TRACE("Loading {}...", fname);
auto fstr = std::make_unique<fs::ifstream>(fs::u8path(fname),
std::ios::binary);
if(!fstr->is_open())
{
ERR("Could not open %s\n", fname.c_str());
ERR("Could not open {}", fname);
return nullptr;
}
stream = std::move(fstr);
}
std::unique_ptr<HrtfStore> hrtf;
std::array<char,GetMarker03Name().size()> magic{};
auto hrtf = std::unique_ptr<HrtfStore>{};
auto magic = std::array<char,HeaderMarkerSize>{};
stream->read(magic.data(), magic.size());
if(stream->gcount() < static_cast<std::streamsize>(GetMarker03Name().size()))
ERR("%.*s data is too short (%zu bytes)\n", al::sizei(name),name.data(), stream->gcount());
if(stream->gcount() < std::streamsize{magic.size()})
ERR("{} data is too short ({} bytes)", name, stream->gcount());
else if(GetMarker03Name() == std::string_view{magic.data(), magic.size()})
{
TRACE("Detected data set format v3\n");
TRACE("Detected data set format v3");
hrtf = LoadHrtf03(*stream);
}
else if(GetMarker02Name() == std::string_view{magic.data(), magic.size()})
{
TRACE("Detected data set format v2\n");
TRACE("Detected data set format v2");
hrtf = LoadHrtf02(*stream);
}
else if(GetMarker01Name() == std::string_view{magic.data(), magic.size()})
{
TRACE("Detected data set format v1\n");
TRACE("Detected data set format v1");
hrtf = LoadHrtf01(*stream);
}
else if(GetMarker00Name() == std::string_view{magic.data(), magic.size()})
{
TRACE("Detected data set format v0\n");
TRACE("Detected data set format v0");
hrtf = LoadHrtf00(*stream);
}
else
ERR("Invalid header in %.*s: \"%.8s\"\n", al::sizei(name), name.data(), magic.data());
ERR("Invalid header in {}: \"{}\"", name, std::string_view{magic.data(), magic.size()});
stream.reset();
if(!hrtf)
@ -1351,8 +1347,7 @@ try {
if(hrtf->mSampleRate != devrate)
{
TRACE("Resampling HRTF %.*s (%uhz -> %uhz)\n", al::sizei(name), name.data(),
hrtf->mSampleRate, devrate);
TRACE("Resampling HRTF {} ({}hz -> {}hz)", name, uint{hrtf->mSampleRate}, devrate);
/* Calculate the last elevation's index and get the total IR count. */
const size_t lastEv{std::accumulate(hrtf->mFields.begin(), hrtf->mFields.end(), 0_uz,
@ -1402,7 +1397,7 @@ try {
float delay_scale{HrirDelayFracOne};
if(max_delay > MaxHrirDelay)
{
WARN("Resampled delay exceeds max (%.2f > %d)\n", max_delay, MaxHrirDelay);
WARN("Resampled delay exceeds max ({:.2f} > {})", max_delay, MaxHrirDelay);
delay_scale *= float{MaxHrirDelay} / max_delay;
}
@ -1424,13 +1419,13 @@ try {
}
handle = LoadedHrtfs.emplace(handle, fname, devrate, std::move(hrtf));
TRACE("Loaded HRTF %.*s for sample rate %uhz, %u-sample filter\n", al::sizei(name),name.data(),
handle->mEntry->mSampleRate, handle->mEntry->mIrSize);
TRACE("Loaded HRTF {} for sample rate {}hz, {}-sample filter", name,
uint{handle->mEntry->mSampleRate}, uint{handle->mEntry->mIrSize});
return HrtfStorePtr{handle->mEntry.get()};
}
catch(std::exception& e) {
ERR("Failed to load %.*s: %s\n", al::sizei(name), name.data(), e.what());
ERR("Failed to load {}: {}", name, e.what());
return nullptr;
}
@ -1438,13 +1433,13 @@ catch(std::exception& e) {
void HrtfStore::add_ref()
{
auto ref = IncrementRef(mRef);
TRACE("HrtfStore %p increasing refcount to %u\n", decltype(std::declval<void*>()){this}, ref);
TRACE("HrtfStore {} increasing refcount to {}", decltype(std::declval<void*>()){this}, ref);
}
void HrtfStore::dec_ref()
{
auto ref = DecrementRef(mRef);
TRACE("HrtfStore %p decreasing refcount to %u\n", decltype(std::declval<void*>()){this}, ref);
TRACE("HrtfStore {} decreasing refcount to {}", decltype(std::declval<void*>()){this}, ref);
if(ref == 0)
{
std::lock_guard<std::mutex> loadlock{LoadedHrtfLock};
@ -1455,7 +1450,7 @@ void HrtfStore::dec_ref()
HrtfStore *entry{hrtf.mEntry.get()};
if(entry && entry->mRef.load() == 0)
{
TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.c_str());
TRACE("Unloading unused HRTF {}", hrtf.mFilename);
hrtf.mEntry = nullptr;
return true;
}

View file

@ -11,7 +11,6 @@
#include "almalloc.h"
#include "alspan.h"
#include "atomic.h"
#include "ambidefs.h"
#include "bufferline.h"
#include "flexarray.h"
@ -20,7 +19,7 @@
struct alignas(16) HrtfStore {
std::atomic<uint> mRef;
std::atomic<uint> mRef{};
uint mSampleRate : 24;
uint mIrSize : 8;
@ -75,7 +74,7 @@ struct DirectHrtfState {
uint mIrSize{0};
al::FlexArray<HrtfChannelState> mChannels;
DirectHrtfState(size_t numchans) : mChannels{numchans} { }
explicit DirectHrtfState(size_t numchans) : mChannels{numchans} { }
/**
* Produces HRTF filter coefficients for decoding B-Format, given a set of
* virtual speaker positions, a matching decoding matrix, and per-order

View file

@ -3,7 +3,6 @@
#include "logging.h"
#include <array>
#include <cctype>
#include <cstdarg>
#include <cstdio>
@ -11,10 +10,10 @@
#include <mutex>
#include <optional>
#include <string>
#include <vector>
#include <string_view>
#include <utility>
#include "alspan.h"
#include "opthelpers.h"
#include "alstring.h"
#include "strutils.h"
@ -36,6 +35,8 @@ LogLevel gLogLevel{LogLevel::Error};
namespace {
using namespace std::string_view_literals;
enum class LogState : uint8_t {
FirstRun,
Ready,
@ -77,57 +78,23 @@ void al_set_log_callback(LogCallbackFunc callback, void *userptr)
}
}
void al_print(LogLevel level, const char *fmt, ...) noexcept
try {
/* Kind of ugly since string literals are const char arrays with a size
* that includes the null terminator, which we want to exclude from the
* span.
*/
auto prefix = al::span{"[ALSOFT] (--) "}.first<14>();
void al_print_impl(LogLevel level, const fmt::string_view fmt, fmt::format_args args)
{
const auto msg = fmt::vformat(fmt, std::move(args));
auto prefix = "[ALSOFT] (--) "sv;
switch(level)
{
case LogLevel::Disable: break;
case LogLevel::Error: prefix = al::span{"[ALSOFT] (EE) "}.first<14>(); break;
case LogLevel::Warning: prefix = al::span{"[ALSOFT] (WW) "}.first<14>(); break;
case LogLevel::Trace: prefix = al::span{"[ALSOFT] (II) "}.first<14>(); break;
case LogLevel::Error: prefix = "[ALSOFT] (EE) "sv; break;
case LogLevel::Warning: prefix = "[ALSOFT] (WW) "sv; break;
case LogLevel::Trace: prefix = "[ALSOFT] (II) "sv; break;
}
std::vector<char> dynmsg;
std::array<char,256> stcmsg{};
char *str{stcmsg.data()};
auto prefend1 = std::copy_n(prefix.begin(), prefix.size(), stcmsg.begin());
al::span<char> msg{prefend1, stcmsg.end()};
/* NOLINTBEGIN(*-array-to-pointer-decay) */
std::va_list args, args2;
va_start(args, fmt);
va_copy(args2, args);
const int msglen{std::vsnprintf(msg.data(), msg.size(), fmt, args)};
if(msglen >= 0)
{
if(static_cast<size_t>(msglen) >= msg.size()) UNLIKELY
{
dynmsg.resize(static_cast<size_t>(msglen)+prefix.size() + 1u);
str = dynmsg.data();
auto prefend2 = std::copy_n(prefix.begin(), prefix.size(), dynmsg.begin());
msg = {prefend2, dynmsg.end()};
std::vsnprintf(msg.data(), msg.size(), fmt, args2);
}
msg = msg.first(static_cast<size_t>(msglen));
}
else
msg = {msg.data(), std::strlen(msg.data())};
va_end(args2);
va_end(args);
/* NOLINTEND(*-array-to-pointer-decay) */
if(gLogLevel >= level)
{
auto logfile = gLogFile;
fputs(str, logfile);
fmt::println(logfile, "{}{}", prefix, msg);
fflush(logfile);
}
#if defined(_WIN32) && !defined(NDEBUG)
@ -135,8 +102,7 @@ try {
* informational, warning, or error debug messages. So only print them for
* non-Release builds.
*/
std::wstring wstr{utf8_to_wstr(str)};
OutputDebugStringW(wstr.c_str());
OutputDebugStringW(utf8_to_wstr(fmt::format("{}{}\n", prefix, msg)).c_str());
#elif defined(__ANDROID__)
auto android_severity = [](LogLevel l) noexcept
{
@ -151,26 +117,20 @@ try {
}
return ANDROID_LOG_ERROR;
};
__android_log_print(android_severity(level), "openal", "%s", str);
/* NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) */
__android_log_print(android_severity(level), "openal", "%.*s%s", al::sizei(prefix),
prefix.data(), msg.c_str());
#endif
auto cblock = std::lock_guard{LogCallbackMutex};
if(gLogState != LogState::Disable)
{
while(!msg.empty() && std::isspace(msg.back()))
{
msg.back() = '\0';
msg = msg.first(msg.size()-1);
}
if(auto logcode = GetLevelCode(level); logcode && !msg.empty())
if(auto logcode = GetLevelCode(level))
{
if(gLogCallback)
gLogCallback(gLogCallbackPtr, *logcode, msg.data(), static_cast<int>(msg.size()));
gLogCallback(gLogCallbackPtr, *logcode, msg.data(), al::sizei(msg));
else if(gLogState == LogState::FirstRun)
gLogState = LogState::Disable;
}
}
}
catch(...) {
/* Swallow any exceptions */
}

View file

@ -3,6 +3,9 @@
#include <cstdio>
#include "fmt/core.h"
#include "opthelpers.h"
enum class LogLevel {
Disable,
@ -10,9 +13,9 @@ enum class LogLevel {
Warning,
Trace
};
extern LogLevel gLogLevel;
DECL_HIDDEN extern LogLevel gLogLevel;
extern FILE *gLogFile;
DECL_HIDDEN extern FILE *gLogFile;
using LogCallbackFunc = void(*)(void *userptr, char level, const char *message, int length) noexcept;
@ -20,12 +23,13 @@ using LogCallbackFunc = void(*)(void *userptr, char level, const char *message,
void al_set_log_callback(LogCallbackFunc callback, void *userptr);
#ifdef __MINGW32__
[[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]]
#else
[[gnu::format(printf,2,3)]]
#endif
void al_print(LogLevel level, const char *fmt, ...) noexcept;
void al_print_impl(LogLevel level, const fmt::string_view fmt, fmt::format_args args);
template<typename ...Args>
void al_print(LogLevel level, fmt::format_string<Args...> fmt, Args&& ...args) noexcept
try {
al_print_impl(level, fmt, fmt::make_format_args(args...));
} catch(...) { }
#define TRACE(...) al_print(LogLevel::Trace, __VA_ARGS__)

View file

@ -19,7 +19,7 @@
/* These structures assume BufferLineSize is a power of 2. */
static_assert((BufferLineSize & (BufferLineSize-1)) == 0, "BufferLineSize is not a power of 2");
struct SlidingHold {
struct SIMDALIGN SlidingHold {
alignas(16) FloatBufferLine mValues;
std::array<uint,BufferLineSize> mExpiries;
uint mLowerIndex;
@ -119,7 +119,8 @@ void Compressor::linkChannels(const uint SamplesToDo,
std::transform(sideChain.begin(), sideChain.end(), buffer.begin(), sideChain.begin(),
max_abs);
};
std::for_each(OutBuffer.begin(), OutBuffer.end(), fill_max);
for(const FloatBufferLine &input : OutBuffer)
fill_max(input);
}
/* This calculates the squared crest factor of the control signal for the
@ -322,10 +323,9 @@ void Compressor::signalDelay(const uint SamplesToDo, const al::span<FloatBufferL
std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const float SampleRate,
const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, const bool AutoPostGain,
const bool AutoDeclip, const float LookAheadTime, const float HoldTime, const float PreGainDb,
const float PostGainDb, const float ThresholdDb, const float Ratio, const float KneeDb,
const float AttackTime, const float ReleaseTime)
const FlagBits autoflags, const float LookAheadTime, const float HoldTime,
const float PreGainDb, const float PostGainDb, const float ThresholdDb, const float Ratio,
const float KneeDb, const float AttackTime, const float ReleaseTime)
{
const auto lookAhead = static_cast<uint>(std::clamp(std::round(LookAheadTime*SampleRate), 0.0f,
BufferLineSize-1.0f));
@ -333,12 +333,11 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa
BufferLineSize-1.0f));
auto Comp = CompressorPtr{new Compressor{}};
Comp->mNumChans = NumChans;
Comp->mAuto.Knee = AutoKnee;
Comp->mAuto.Attack = AutoAttack;
Comp->mAuto.Release = AutoRelease;
Comp->mAuto.PostGain = AutoPostGain;
Comp->mAuto.Declip = AutoPostGain && AutoDeclip;
Comp->mAuto.Knee = autoflags.test(AutoKnee);
Comp->mAuto.Attack = autoflags.test(AutoAttack);
Comp->mAuto.Release = autoflags.test(AutoRelease);
Comp->mAuto.PostGain = autoflags.test(AutoPostGain);
Comp->mAuto.Declip = autoflags.test(AutoPostGain) && autoflags.test(AutoDeclip);
Comp->mLookAhead = lookAhead;
Comp->mPreGain = std::pow(10.0f, PreGainDb / 20.0f);
Comp->mPostGain = std::log(10.0f)/20.0f * PostGainDb;
@ -381,15 +380,11 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa
Compressor::~Compressor() = default;
void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer)
void Compressor::process(const uint SamplesToDo, const al::span<FloatBufferLine> InOut)
{
const size_t numChans{mNumChans};
ASSUME(SamplesToDo > 0);
ASSUME(SamplesToDo <= BufferLineSize);
ASSUME(numChans > 0);
const auto output = al::span{OutBuffer, numChans};
const float preGain{mPreGain};
if(preGain != 1.0f)
{
@ -399,10 +394,10 @@ void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer)
std::transform(buffer.cbegin(), buffer.cend(), buffer.begin(),
[preGain](const float s) noexcept { return s * preGain; });
};
std::for_each(output.begin(), output.end(), apply_gain);
std::for_each(InOut.begin(), InOut.end(), apply_gain);
}
linkChannels(SamplesToDo, output);
linkChannels(SamplesToDo, InOut);
if(mAuto.Attack || mAuto.Release)
crestDetector(SamplesToDo);
@ -415,16 +410,17 @@ void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer)
gainCompressor(SamplesToDo);
if(!mDelay.empty())
signalDelay(SamplesToDo, output);
signalDelay(SamplesToDo, InOut);
const auto gains = assume_aligned_span<16>(al::span{mSideChain}.first(SamplesToDo));
auto apply_comp = [gains](const FloatBufferSpan input) noexcept -> void
auto apply_comp = [gains](const FloatBufferSpan inout) noexcept -> void
{
const auto buffer = assume_aligned_span<16>(input);
const auto buffer = assume_aligned_span<16>(inout);
std::transform(gains.cbegin(), gains.cend(), buffer.cbegin(), buffer.begin(),
std::multiplies{});
};
std::for_each(output.begin(), output.end(), apply_comp);
for(const FloatBufferSpan inout : InOut)
apply_comp(inout);
const auto delayedGains = al::span{mSideChain}.subspan(SamplesToDo, mLookAhead);
std::copy(delayedGains.begin(), delayedGains.end(), mSideChain.begin());

View file

@ -2,12 +2,13 @@
#define CORE_MASTERING_H
#include <array>
#include <bitset>
#include <memory>
#include "almalloc.h"
#include "alnumeric.h"
#include "alspan.h"
#include "bufferline.h"
#include "opthelpers.h"
#include "vector.h"
struct SlidingHold;
@ -25,9 +26,7 @@ using uint = unsigned int;
*
* http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
*/
class Compressor {
size_t mNumChans{0u};
class SIMDALIGN Compressor {
struct AutoFlags {
bool Knee : 1;
bool Attack : 1;
@ -75,8 +74,13 @@ class Compressor {
void signalDelay(const uint SamplesToDo, const al::span<FloatBufferLine> OutBuffer);
public:
enum {
AutoKnee, AutoAttack, AutoRelease, AutoPostGain, AutoDeclip, FlagsCount
};
using FlagBits = std::bitset<FlagsCount>;
~Compressor();
void process(const uint SamplesToDo, FloatBufferLine *OutBuffer);
void process(const uint SamplesToDo, al::span<FloatBufferLine> InOut);
[[nodiscard]] auto getLookAhead() const noexcept -> uint { return mLookAhead; }
/**
@ -106,11 +110,9 @@ public:
* automating release time.
*/
static std::unique_ptr<Compressor> Create(const size_t NumChans, const float SampleRate,
const bool AutoKnee, const bool AutoAttack, const bool AutoRelease,
const bool AutoPostGain, const bool AutoDeclip, const float LookAheadTime,
const float HoldTime, const float PreGainDb, const float PostGainDb,
const float ThresholdDb, const float Ratio, const float KneeDb, const float AttackTime,
const float ReleaseTime);
const FlagBits autoflags, const float LookAheadTime, const float HoldTime,
const float PreGainDb, const float PostGainDb, const float ThresholdDb, const float Ratio,
const float KneeDb, const float AttackTime, const float ReleaseTime);
};
using CompressorPtr = std::unique_ptr<Compressor>;

View file

@ -8,6 +8,7 @@
#include "alspan.h"
#include "ambidefs.h"
#include "bufferline.h"
#include "opthelpers.h"
struct MixParams;
@ -16,7 +17,7 @@ using MixerOutFunc = void(*)(const al::span<const float> InSamples,
const al::span<FloatBufferLine> OutBuffer, const al::span<float> CurrentGains,
const al::span<const float> TargetGains, const std::size_t Counter, const std::size_t OutPos);
extern MixerOutFunc MixSamplesOut;
DECL_HIDDEN extern MixerOutFunc MixSamplesOut;
inline void MixSamples(const al::span<const float> InSamples,
const al::span<FloatBufferLine> OutBuffer, const al::span<float> CurrentGains,
const al::span<const float> TargetGains, const std::size_t Counter, const std::size_t OutPos)
@ -26,7 +27,7 @@ inline void MixSamples(const al::span<const float> InSamples,
using MixerOneFunc = void(*)(const al::span<const float> InSamples,const al::span<float> OutBuffer,
float &CurrentGain, const float TargetGain, const std::size_t Counter);
extern MixerOneFunc MixSamplesOne;
DECL_HIDDEN extern MixerOneFunc MixSamplesOne;
inline void MixSamples(const al::span<const float> InSamples, const al::span<float> OutBuffer,
float &CurrentGain, const float TargetGain, const std::size_t Counter)
{ MixSamplesOne(InSamples, OutBuffer, CurrentGain, TargetGain, Counter); }

View file

@ -36,8 +36,10 @@ enum class Resampler : std::uint8_t {
BSinc12,
FastBSinc24,
BSinc24,
FastBSinc48,
BSinc48,
Max = BSinc24
Max = BSinc48
};
/* Interpolator state. Kind of a misnomer since the interpolator itself is
@ -60,7 +62,7 @@ struct CubicState {
* each subsequent phase index follows contiguously.
*/
al::span<const CubicCoefficients,CubicPhaseCount> filter;
CubicState(al::span<const CubicCoefficients,CubicPhaseCount> f) : filter{f} { }
explicit CubicState(al::span<const CubicCoefficients,CubicPhaseCount> f) : filter{f} { }
};
using InterpState = std::variant<std::monostate,CubicState,BsincState>;

View file

@ -19,6 +19,7 @@
#include "hrtfbase.h"
#include "opthelpers.h"
struct CTag;
struct NEONTag;
struct LerpTag;
struct CubicTag;
@ -76,13 +77,16 @@ inline void ApplyCoeffs(const al::span<float2> Values, const size_t IrSize,
return vcombine_f32(leftright2, leftright2);
};
const auto leftright4 = dup_samples();
const auto count4 = size_t{(IrSize+1) >> 1};
const auto vals4 = al::span{reinterpret_cast<float32x4_t*>(Values[0].data()), count4};
const auto coeffs4 = al::span{reinterpret_cast<const float32x4_t*>(Coeffs[0].data()), count4};
std::transform(vals4.cbegin(), vals4.cend(), coeffs4.cbegin(), vals4.begin(),
[leftright4](const float32x4_t &val, const float32x4_t &coeff) -> float32x4_t
{ return vmlaq_f32(val, coeff, leftright4); });
/* Using a loop here instead of std::transform since some builds seem to
* have an issue with accessing an array/span of float32x4_t.
*/
for(size_t c{0};c < IrSize;c += 2)
{
auto vals = vld1q_f32(&Values[c][0]);
vals = vmlaq_f32(vals, vld1q_f32(&Coeffs[c][0]), leftright4);
vst1q_f32(&Values[c][0], vals);
}
}
force_inline void MixLine(const al::span<const float> InSamples, const al::span<float> dst,
@ -461,6 +465,9 @@ void Mix_<NEONTag>(const al::span<const float> InSamples,const al::span<FloatBuf
const al::span<float> CurrentGains, const al::span<const float> TargetGains,
const size_t Counter, const size_t OutPos)
{
if((OutPos&3) != 0) UNLIKELY
return Mix_<CTag>(InSamples, OutBuffer, CurrentGains, TargetGains, Counter, OutPos);
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto fade_len = std::min(Counter, InSamples.size());
const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len;
@ -476,6 +483,9 @@ template<>
void Mix_<NEONTag>(const al::span<const float> InSamples, const al::span<float> OutBuffer,
float &CurrentGain, const float TargetGain, const size_t Counter)
{
if((reinterpret_cast<uintptr_t>(OutBuffer.data())&15) != 0) UNLIKELY
return Mix_<CTag>(InSamples, OutBuffer, CurrentGain, TargetGain, Counter);
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto fade_len = std::min(Counter, InSamples.size());
const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len;

View file

@ -21,6 +21,7 @@
#include "hrtfbase.h"
#include "opthelpers.h"
struct CTag;
struct SSETag;
struct CubicTag;
struct BSincTag;
@ -105,8 +106,8 @@ force_inline void MixLine(const al::span<const float> InSamples, const al::span<
size_t pos{0};
if(std::abs(step) > std::numeric_limits<float>::epsilon())
{
const auto gain = float{CurrentGain};
auto step_count = float{0.0f};
const auto gain = CurrentGain;
auto step_count = 0.0f;
/* Mix with applying gain steps in aligned multiples of 4. */
if(const size_t todo{fade_len >> 2})
{
@ -363,6 +364,9 @@ void Mix_<SSETag>(const al::span<const float> InSamples, const al::span<FloatBuf
const al::span<float> CurrentGains, const al::span<const float> TargetGains,
const size_t Counter, const size_t OutPos)
{
if((OutPos&3) != 0) UNLIKELY
return Mix_<CTag>(InSamples, OutBuffer, CurrentGains, TargetGains, Counter, OutPos);
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto fade_len = std::min(Counter, InSamples.size());
const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len;
@ -378,6 +382,9 @@ template<>
void Mix_<SSETag>(const al::span<const float> InSamples, const al::span<float> OutBuffer,
float &CurrentGain, const float TargetGain, const size_t Counter)
{
if((reinterpret_cast<uintptr_t>(OutBuffer.data())&15) != 0) UNLIKELY
return Mix_<CTag>(InSamples, OutBuffer, CurrentGain, TargetGain, Counter);
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto fade_len = std::min(Counter, InSamples.size());
const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len;

View file

@ -69,6 +69,7 @@ namespace {
inline pid_t _gettid()
{
#ifdef __linux__
/* NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) */
return static_cast<pid_t>(syscall(SYS_gettid));
#elif defined(__FreeBSD__)
long pid{};

View file

@ -4,45 +4,49 @@
#include "storage_formats.h"
#include <cstdint>
#include <string_view>
namespace {
using namespace std::string_view_literals;
} // namespace
const char *NameFromFormat(FmtType type) noexcept
auto NameFromFormat(FmtType type) noexcept -> std::string_view
{
switch(type)
{
case FmtUByte: return "UInt8";
case FmtShort: return "Int16";
case FmtInt: return "Int32";
case FmtFloat: return "Float";
case FmtDouble: return "Double";
case FmtMulaw: return "muLaw";
case FmtAlaw: return "aLaw";
case FmtIMA4: return "IMA4 ADPCM";
case FmtMSADPCM: return "MS ADPCM";
case FmtUByte: return "UInt8"sv;
case FmtShort: return "Int16"sv;
case FmtInt: return "Int32"sv;
case FmtFloat: return "Float"sv;
case FmtDouble: return "Double"sv;
case FmtMulaw: return "muLaw"sv;
case FmtAlaw: return "aLaw"sv;
case FmtIMA4: return "IMA4 ADPCM"sv;
case FmtMSADPCM: return "MS ADPCM"sv;
}
return "<internal error>";
return "<internal error>"sv;
}
const char *NameFromFormat(FmtChannels channels) noexcept
auto NameFromFormat(FmtChannels channels) noexcept -> std::string_view
{
switch(channels)
{
case FmtMono: return "Mono";
case FmtStereo: return "Stereo";
case FmtRear: return "Rear";
case FmtQuad: return "Quadraphonic";
case FmtX51: return "Surround 5.1";
case FmtX61: return "Surround 6.1";
case FmtX71: return "Surround 7.1";
case FmtBFormat2D: return "B-Format 2D";
case FmtBFormat3D: return "B-Format 3D";
case FmtUHJ2: return "UHJ2";
case FmtUHJ3: return "UHJ3";
case FmtUHJ4: return "UHJ4";
case FmtSuperStereo: return "Super Stereo";
case FmtMonoDup: return "Mono (dup)";
case FmtMono: return "Mono"sv;
case FmtStereo: return "Stereo"sv;
case FmtRear: return "Rear"sv;
case FmtQuad: return "Quadraphonic"sv;
case FmtX51: return "Surround 5.1"sv;
case FmtX61: return "Surround 6.1"sv;
case FmtX71: return "Surround 7.1"sv;
case FmtBFormat2D: return "B-Format 2D"sv;
case FmtBFormat3D: return "B-Format 3D"sv;
case FmtUHJ2: return "UHJ2"sv;
case FmtUHJ3: return "UHJ3"sv;
case FmtUHJ4: return "UHJ4"sv;
case FmtSuperStereo: return "Super Stereo"sv;
case FmtMonoDup: return "Mono (dup)"sv;
}
return "<internal error>";
return "<internal error>"sv;
}
uint BytesFromFmt(FmtType type) noexcept

View file

@ -1,6 +1,8 @@
#ifndef CORE_STORAGE_FORMATS_H
#define CORE_STORAGE_FORMATS_H
#include <string_view>
using uint = unsigned int;
/* Storable formats */
@ -43,8 +45,8 @@ enum class AmbiScaling : unsigned char {
UHJ,
};
const char *NameFromFormat(FmtType type) noexcept;
const char *NameFromFormat(FmtChannels channels) noexcept;
auto NameFromFormat(FmtType type) noexcept -> std::string_view;
auto NameFromFormat(FmtChannels channels) noexcept -> std::string_view;
uint BytesFromFmt(FmtType type) noexcept;
uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept;

View file

@ -70,14 +70,13 @@ struct SegmentedFilter {
auto tmpBuffer = std::vector<double>(fft_size, 0.0);
for(std::size_t i{0};i < fft_size/2;++i)
{
const int k{int{fft_size/2} - static_cast<int>(i*2 + 1)};
const auto k = int{fft_size/2} - static_cast<int>(i*2 + 1);
const double w{2.0*al::numbers::pi * static_cast<double>(i*2 + 1)
/ double{fft_size}};
const double window{0.3635819 - 0.4891775*std::cos(w) + 0.1365995*std::cos(2.0*w)
- 0.0106411*std::cos(3.0*w)};
const auto w = 2.0*al::numbers::pi/double{fft_size} * static_cast<double>(i*2 + 1);
const auto window = 0.3635819 - 0.4891775*std::cos(w) + 0.1365995*std::cos(2.0*w)
- 0.0106411*std::cos(3.0*w);
const double pk{al::numbers::pi * static_cast<double>(k)};
const auto pk = al::numbers::pi * static_cast<double>(k);
tmpBuffer[i*2 + 1] = window * (1.0-std::cos(pk)) / pk;
}
@ -131,11 +130,10 @@ constexpr std::array<float,4> Filter2Coeff{{
0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156684f
}};
} // namespace
void UhjAllPassFilter::processOne(const al::span<const float, 4> coeffs, float x)
void processOne(UhjAllPassFilter &self, const al::span<const float, 4> coeffs, float x)
{
auto state = mState;
auto state = self.mState;
for(size_t i{0};i < 4;++i)
{
const float y{x*coeffs[i] + state[i].z[0]};
@ -143,13 +141,13 @@ void UhjAllPassFilter::processOne(const al::span<const float, 4> coeffs, float x
state[i].z[1] = y*coeffs[i] - x;
x = y;
}
mState = state;
self.mState = state;
}
void UhjAllPassFilter::process(const al::span<const float,4> coeffs,
void process(UhjAllPassFilter &self, const al::span<const float,4> coeffs,
const al::span<const float> src, const bool updateState, const al::span<float> dst)
{
auto state = mState;
auto state = self.mState;
auto proc_sample = [&state,coeffs](float x) noexcept -> float
{
@ -163,9 +161,10 @@ void UhjAllPassFilter::process(const al::span<const float,4> coeffs,
return x;
};
std::transform(src.begin(), src.end(), dst.begin(), proc_sample);
if(updateState) LIKELY mState = state;
if(updateState) LIKELY self.mState = state;
}
} // namespace
/* Encoding UHJ from B-Format is done as:
*
@ -216,7 +215,9 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
const size_t todo{std::min(sSegmentSize-mFifoPos, SamplesToDo-base)};
auto wseg = winput.subspan(base, todo);
auto xseg = xinput.subspan(base, todo);
auto wxio = al::span{mWXInOut}.subspan(mFifoPos, todo);
/* Some Clang versions don't like calling subspan on an rvalue here. */
const auto wxio_ = al::span{mWXInOut};
auto wxio = wxio_.subspan(mFifoPos, todo);
/* Copy out the samples that were previously processed by the FFT. */
dstore = std::copy_n(wxio.begin(), todo, dstore);
@ -351,17 +352,17 @@ void UhjEncoderIIR::encode(float *LeftOut, float *RightOut,
/* S = 0.9396926*W + 0.1855740*X */
std::transform(winput.begin(), winput.end(), xinput.begin(), mTemp.begin(),
[](const float w, const float x) noexcept { return 0.9396926f*w + 0.1855740f*x; });
mFilter1WX.process(Filter1Coeff, al::span{mTemp}.first(SamplesToDo), true,
process(mFilter1WX, Filter1Coeff, al::span{mTemp}.first(SamplesToDo), true,
al::span{mS}.subspan(1));
mS[0] = mDelayWX; mDelayWX = mS[SamplesToDo];
/* Precompute j(-0.3420201*W + 0.5098604*X) and store in mWX. */
std::transform(winput.begin(), winput.end(), xinput.begin(), mTemp.begin(),
[](const float w, const float x) noexcept { return -0.3420201f*w + 0.5098604f*x; });
mFilter2WX.process(Filter2Coeff, al::span{mTemp}.first(SamplesToDo), true, mWX);
process(mFilter2WX, Filter2Coeff, al::span{mTemp}.first(SamplesToDo), true, mWX);
/* Apply filter1 to Y and store in mD. */
mFilter1Y.process(Filter1Coeff, yinput, true, al::span{mD}.subspan(1));
process(mFilter1Y, Filter1Coeff, yinput, true, al::span{mD}.subspan(1));
mD[0] = mDelayY; mDelayY = mD[SamplesToDo];
/* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y */
@ -371,21 +372,19 @@ void UhjEncoderIIR::encode(float *LeftOut, float *RightOut,
/* Apply the base filter to the existing output to align with the processed
* signal.
*/
mFilter1Direct[0].process(Filter1Coeff, {LeftOut, SamplesToDo}, true,
al::span{mTemp}.subspan(1));
const auto left = al::span{al::assume_aligned<16>(LeftOut), SamplesToDo};
process(mFilter1Direct[0], Filter1Coeff, left, true, al::span{mTemp}.subspan(1));
mTemp[0] = mDirectDelay[0]; mDirectDelay[0] = mTemp[SamplesToDo];
/* Left = (S + D)/2.0 */
const auto left = al::span{al::assume_aligned<16>(LeftOut), SamplesToDo};
for(size_t i{0};i < SamplesToDo;++i)
left[i] = (mS[i] + mD[i])*0.5f + mTemp[i];
mFilter1Direct[1].process(Filter1Coeff, {RightOut, SamplesToDo}, true,
al::span{mTemp}.subspan(1));
const auto right = al::span{al::assume_aligned<16>(RightOut), SamplesToDo};
process(mFilter1Direct[1], Filter1Coeff, right, true, al::span{mTemp}.subspan(1));
mTemp[0] = mDirectDelay[1]; mDirectDelay[1] = mTemp[SamplesToDo];
/* Right = (S - D)/2.0 */
const auto right = al::span{al::assume_aligned<16>(RightOut), SamplesToDo};
for(size_t i{0};i < SamplesToDo;++i)
right[i] = (mS[i] - mD[i])*0.5f + mTemp[i];
}
@ -497,11 +496,12 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
std::transform(mD.cbegin(), mD.cbegin()+sInputPadding+samplesToDo, youtput.begin(),
mTemp.begin(),
[](const float d, const float t) noexcept { return 0.828331f*d + 0.767820f*t; });
if(mFirstRun) mFilter2DT.processOne(Filter2Coeff, mTemp[0]);
mFilter2DT.process(Filter2Coeff, al::span{mTemp}.subspan(1,samplesToDo), updateState, xoutput);
if(mFirstRun) processOne(mFilter2DT, Filter2Coeff, mTemp[0]);
process(mFilter2DT, Filter2Coeff, al::span{mTemp}.subspan(1, samplesToDo), updateState,
xoutput);
/* Apply filter1 to S and store in mTemp. */
mFilter1S.process(Filter1Coeff, al::span{mS}.first(samplesToDo), updateState, mTemp);
process(mFilter1S, Filter1Coeff, al::span{mS}.first(samplesToDo), updateState, mTemp);
/* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */
std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), woutput.begin(),
@ -514,11 +514,11 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
/* Apply filter1 to (0.795968*D - 0.676392*T) and store in mTemp. */
std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput.begin(), youtput.begin(),
[](const float d, const float t) noexcept { return 0.795968f*d - 0.676392f*t; });
mFilter1DT.process(Filter1Coeff, youtput.first(samplesToDo), updateState, mTemp);
process(mFilter1DT, Filter1Coeff, youtput.first(samplesToDo), updateState, mTemp);
/* Precompute j*S and store in youtput. */
if(mFirstRun) mFilter2S.processOne(Filter2Coeff, mS[0]);
mFilter2S.process(Filter2Coeff, al::span{mS}.subspan(1, samplesToDo), updateState, youtput);
if(mFirstRun) processOne(mFilter2S, Filter2Coeff, mS[0]);
process(mFilter2S, Filter2Coeff, al::span{mS}.subspan(1, samplesToDo), updateState, youtput);
/* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */
std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, youtput.begin(), youtput.begin(),
@ -529,7 +529,7 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
const auto zoutput = al::span{al::assume_aligned<16>(samples[3]), samplesToDo};
/* Apply filter1 to Q and store in mTemp. */
mFilter1Q.process(Filter1Coeff, zoutput, updateState, mTemp);
process(mFilter1Q, Filter1Coeff, zoutput, updateState, mTemp);
/* Z = 1.023332*Q */
std::transform(mTemp.begin(), mTemp.end(), zoutput.begin(),
@ -545,9 +545,9 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
* S = Left + Right
* D = Left - Right
*
* W = 0.6098637*S - 0.6896511*j*w*D
* X = 0.8624776*S + 0.7626955*j*w*D
* Y = 1.6822415*w*D - 0.2156194*j*S
* W = 0.6098637*S + 0.6896511*j*w*D
* X = 0.8624776*S - 0.7626955*j*w*D
* Y = 1.6822415*w*D + 0.2156194*j*S
*
* where j is a +90 degree phase shift. w is a variable control for the
* resulting stereo width, with the range 0 <= w <= 0.7.
@ -613,12 +613,12 @@ void UhjStereoDecoder<N>::decode(const al::span<float*> samples, const size_t sa
std::copy_n(mTemp.cbegin()+samplesToDo, mDTHistory.size(), mDTHistory.begin());
PShift.process(xoutput, mTemp);
/* W = 0.6098637*S - 0.6896511*j*w*D */
/* W = 0.6098637*S + 0.6896511*j*w*D */
std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), woutput.begin(),
[](const float s, const float jd) noexcept { return 0.6098637f*s - 0.6896511f*jd; });
/* X = 0.8624776*S + 0.7626955*j*w*D */
[](const float s, const float jd) noexcept { return 0.6098637f*s + 0.6896511f*jd; });
/* X = 0.8624776*S - 0.7626955*j*w*D */
std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), xoutput.begin(),
[](const float s, const float jd) noexcept { return 0.8624776f*s + 0.7626955f*jd; });
[](const float s, const float jd) noexcept { return 0.8624776f*s - 0.7626955f*jd; });
/* Precompute j*S and store in youtput. */
tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin());
@ -627,9 +627,9 @@ void UhjStereoDecoder<N>::decode(const al::span<float*> samples, const size_t sa
std::copy_n(mTemp.cbegin()+samplesToDo, mSHistory.size(), mSHistory.begin());
PShift.process(youtput, mTemp);
/* Y = 1.6822415*w*D - 0.2156194*j*S */
/* Y = 1.6822415*w*D + 0.2156194*j*S */
std::transform(mD.begin(), mD.begin()+samplesToDo, youtput.begin(), youtput.begin(),
[](const float d, const float js) noexcept { return 1.6822415f*d - 0.2156194f*js; });
[](const float d, const float js) noexcept { return 1.6822415f*d + 0.2156194f*js; });
}
void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t samplesToDo,
@ -685,29 +685,29 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
const auto youtput = al::span{al::assume_aligned<16>(samples[2]), samplesToDo};
/* Apply filter1 to S and store in mTemp. */
mFilter1S.process(Filter1Coeff, al::span{mS}.first(samplesToDo), updateState, mTemp);
process(mFilter1S, Filter1Coeff, al::span{mS}.first(samplesToDo), updateState, mTemp);
/* Precompute j*D and store in xoutput. */
if(mFirstRun) mFilter2D.processOne(Filter2Coeff, mD[0]);
mFilter2D.process(Filter2Coeff, al::span{mD}.subspan(1, samplesToDo), updateState, xoutput);
if(mFirstRun) processOne(mFilter2D, Filter2Coeff, mD[0]);
process(mFilter2D, Filter2Coeff, al::span{mD}.subspan(1, samplesToDo), updateState, xoutput);
/* W = 0.6098637*S - 0.6896511*j*w*D */
/* W = 0.6098637*S + 0.6896511*j*w*D */
std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), woutput.begin(),
[](const float s, const float jd) noexcept { return 0.6098637f*s - 0.6896511f*jd; });
/* X = 0.8624776*S + 0.7626955*j*w*D */
[](const float s, const float jd) noexcept { return 0.6098637f*s + 0.6896511f*jd; });
/* X = 0.8624776*S - 0.7626955*j*w*D */
std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), xoutput.begin(),
[](const float s, const float jd) noexcept { return 0.8624776f*s + 0.7626955f*jd; });
[](const float s, const float jd) noexcept { return 0.8624776f*s - 0.7626955f*jd; });
/* Precompute j*S and store in youtput. */
if(mFirstRun) mFilter2S.processOne(Filter2Coeff, mS[0]);
mFilter2S.process(Filter2Coeff, al::span{mS}.subspan(1, samplesToDo), updateState, youtput);
if(mFirstRun) processOne(mFilter2S, Filter2Coeff, mS[0]);
process(mFilter2S, Filter2Coeff, al::span{mS}.subspan(1, samplesToDo), updateState, youtput);
/* Apply filter1 to D and store in mTemp. */
mFilter1D.process(Filter1Coeff, al::span{mD}.first(samplesToDo), updateState, mTemp);
process(mFilter1D, Filter1Coeff, al::span{mD}.first(samplesToDo), updateState, mTemp);
/* Y = 1.6822415*w*D - 0.2156194*j*S */
/* Y = 1.6822415*w*D + 0.2156194*j*S */
std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, youtput.begin(), youtput.begin(),
[](const float d, const float js) noexcept { return 1.6822415f*d - 0.2156194f*js; });
[](const float d, const float js) noexcept { return 1.6822415f*d + 0.2156194f*js; });
mFirstRun = false;
}

View file

@ -7,6 +7,7 @@
#include "alspan.h"
#include "bufferline.h"
#include "opthelpers.h"
inline constexpr std::size_t UhjLength256{256};
@ -29,14 +30,10 @@ struct UhjAllPassFilter {
std::array<float,2> z{};
};
std::array<AllPassState,4> mState;
void processOne(const al::span<const float,4> coeffs, float x);
void process(const al::span<const float,4> coeffs, const al::span<const float> src,
const bool update, const al::span<float> dst);
};
struct UhjEncoderBase {
struct SIMDALIGN UhjEncoderBase {
UhjEncoderBase() = default;
UhjEncoderBase(const UhjEncoderBase&) = delete;
UhjEncoderBase(UhjEncoderBase&&) = delete;
@ -120,7 +117,7 @@ struct UhjEncoderIIR final : public UhjEncoderBase {
};
struct DecoderBase {
struct SIMDALIGN DecoderBase {
static constexpr std::size_t sMaxPadding{256};
/* For 2-channel UHJ, shelf filters should use these LF responses. */

View file

@ -1,6 +1,6 @@
#include "config.h"
#include "config_backends.h"
#ifndef AL_NO_UID_DEFS
@ -16,7 +16,7 @@ DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x0
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
#if defined(HAVE_WASAPI) && !defined(ALSOFT_UWP)
#if HAVE_WASAPI && !ALSOFT_UWP
#include <wtypes.h>
#include <devpropdef.h>
#include <propkeydef.h>

View file

@ -1,5 +1,6 @@
#include "config.h"
#include "config_simd.h"
#include "voice.h"
@ -42,10 +43,10 @@
#include "voice_change.h"
struct CTag;
#ifdef HAVE_SSE
#if HAVE_SSE
struct SSETag;
#endif
#ifdef HAVE_NEON
#if HAVE_NEON
struct NEONTag;
#endif
@ -75,11 +76,11 @@ HrtfMixerBlendFunc MixHrtfBlendSamples{MixHrtfBlend_<CTag>};
inline MixerOutFunc SelectMixer()
{
#ifdef HAVE_NEON
#if HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Mix_<NEONTag>;
#endif
#ifdef HAVE_SSE
#if HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return Mix_<SSETag>;
#endif
@ -88,11 +89,11 @@ inline MixerOutFunc SelectMixer()
inline MixerOneFunc SelectMixerOne()
{
#ifdef HAVE_NEON
#if HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Mix_<NEONTag>;
#endif
#ifdef HAVE_SSE
#if HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return Mix_<SSETag>;
#endif
@ -101,11 +102,11 @@ inline MixerOneFunc SelectMixerOne()
inline HrtfMixerFunc SelectHrtfMixer()
{
#ifdef HAVE_NEON
#if HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtf_<NEONTag>;
#endif
#ifdef HAVE_SSE
#if HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtf_<SSETag>;
#endif
@ -114,11 +115,11 @@ inline HrtfMixerFunc SelectHrtfMixer()
inline HrtfMixerBlendFunc SelectHrtfBlendMixer()
{
#ifdef HAVE_NEON
#if HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtfBlend_<NEONTag>;
#endif
#ifdef HAVE_SSE
#if HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtfBlend_<SSETag>;
#endif
@ -145,19 +146,26 @@ void Voice::InitMixer(std::optional<std::string> resopt)
ResamplerEntry{"fast_bsinc12"sv, Resampler::FastBSinc12},
ResamplerEntry{"bsinc24"sv, Resampler::BSinc24},
ResamplerEntry{"fast_bsinc24"sv, Resampler::FastBSinc24},
ResamplerEntry{"bsinc48"sv, Resampler::BSinc48},
ResamplerEntry{"fast_bsinc48"sv, Resampler::FastBSinc48},
};
std::string_view resampler{*resopt};
if(al::case_compare(resampler, "cubic"sv) == 0
|| al::case_compare(resampler, "sinc4"sv) == 0
if (al::case_compare(resampler, "cubic"sv) == 0)
{
WARN("Resampler option \"{}\" is deprecated, using spline", *resopt);
resampler = "spline"sv;
}
else if(al::case_compare(resampler, "sinc4"sv) == 0
|| al::case_compare(resampler, "sinc8"sv) == 0)
{
WARN("Resampler option \"%s\" is deprecated, using gaussian\n", resopt->c_str());
WARN("Resampler option \"{}\" is deprecated, using gaussian", *resopt);
resampler = "gaussian"sv;
}
else if(al::case_compare(resampler, "bsinc"sv) == 0)
{
WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", resopt->c_str());
WARN("Resampler option \"{}\" is deprecated, using bsinc12", *resopt);
resampler = "bsinc12"sv;
}
@ -165,7 +173,7 @@ void Voice::InitMixer(std::optional<std::string> resopt)
[resampler](const ResamplerEntry &entry) -> bool
{ return al::case_compare(resampler, entry.name) == 0; });
if(iter == ResamplerList.end())
ERR("Invalid resampler: %s\n", resopt->c_str());
ERR("Invalid resampler: {}", *resopt);
else
ResamplerDefault = iter->resampler;
}
@ -199,7 +207,7 @@ constexpr std::array<int,16> IMA4Codeword{{
}};
/* IMA4 ADPCM Step index adjust decode table */
constexpr std::array<int,16>IMA4Index_adjust{{
constexpr std::array<int,16> IMA4Index_adjust{{
-1,-1,-1,-1, 2, 4, 6, 8,
-1,-1,-1,-1, 2, 4, 6, 8
}};
@ -226,9 +234,9 @@ void SendSourceStoppedEvent(ContextBase *context, uint id)
{
RingBuffer *ring{context->mAsyncEvents.get()};
auto evt_vec = ring->getWriteVector();
if(evt_vec.first.len < 1) return;
if(evt_vec[0].len < 1) return;
auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec.first.buf);
auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec[0].buf);
evt.mId = id;
evt.mState = AsyncSrcState::Stop;
@ -270,18 +278,17 @@ inline void LoadSamples(const al::span<float> dstSamples, const al::span<const s
{
using TypeTraits = al::FmtTypeTraits<Type>;
using SampleType = typename TypeTraits::Type;
static constexpr size_t sampleSize{sizeof(SampleType)};
assert(srcChan < srcStep);
auto converter = TypeTraits{};
al::span<const SampleType> src{reinterpret_cast<const SampleType*>(srcData.data()),
srcData.size()/sampleSize};
auto ssrc = src.cbegin() + ptrdiff_t(srcOffset*srcStep);
std::generate(dstSamples.begin(), dstSamples.end(), [&ssrc,srcChan,srcStep,converter]
srcData.size()/sizeof(SampleType)};
auto ssrc = src.cbegin() + ptrdiff_t(srcOffset*srcStep + srcChan);
dstSamples.front() = converter(*ssrc);
std::generate(dstSamples.begin()+1, dstSamples.end(), [&ssrc,srcStep,converter]
{
auto ret = converter(ssrc[srcChan]);
ssrc += ptrdiff_t(srcStep);
return ret;
return converter(*ssrc);
});
}
@ -303,46 +310,53 @@ inline void LoadSamples<FmtIMA4>(al::span<float> dstSamples, al::span<const std:
size_t skip{srcOffset % samplesPerBlock};
/* NOTE: This could probably be optimized better. */
while(!dstSamples.empty())
auto dst = dstSamples.begin();
while(dst != dstSamples.end())
{
auto nibbleData = src.cbegin();
src = src.subspan(blockBytes);
/* Each IMA4 block starts with a signed 16-bit sample, and a signed
/* Each IMA4 block starts with a signed 16-bit sample, and a signed(?)
* 16-bit table index. The table index needs to be clamped.
*/
int sample{int(nibbleData[srcChan*4]) | (int(nibbleData[srcChan*4 + 1]) << 8)};
int index{int(nibbleData[srcChan*4 + 2]) | (int(nibbleData[srcChan*4 + 3]) << 8)};
nibbleData += ptrdiff_t((srcStep+srcChan)*4);
auto prevSample = int(src[srcChan*4 + 0]) | (int(src[srcChan*4 + 1]) << 8);
auto prevIndex = int(src[srcChan*4 + 2]) | (int(src[srcChan*4 + 3]) << 8);
const auto nibbleData = src.subspan((srcStep+srcChan)*4);
src = src.subspan(blockBytes);
sample = (sample^0x8000) - 32768;
index = std::clamp((index^0x8000) - 32768, 0, MaxStepIndex);
/* Sign-extend the 16-bit sample and index values. */
prevSample = (prevSample^0x8000) - 32768;
prevIndex = std::clamp((prevIndex^0x8000) - 32768, 0, MaxStepIndex);
if(skip == 0)
{
dstSamples[0] = static_cast<float>(sample) / 32768.0f;
dstSamples = dstSamples.subspan<1>();
if(dstSamples.empty()) return;
*dst = static_cast<float>(prevSample) / 32768.0f;
if(++dst == dstSamples.end()) return;
}
else
--skip;
auto decode_sample = [&sample,&index](const uint nibble)
{
sample += IMA4Codeword[nibble] * IMAStep_size[static_cast<uint>(index)] / 8;
sample = std::clamp(sample, -32768, 32767);
index += IMA4Index_adjust[nibble];
index = std::clamp(index, 0, MaxStepIndex);
return sample;
};
/* The rest of the block is arranged as a series of nibbles, contained
* in 4 *bytes* per channel interleaved. So every 8 nibbles we need to
* skip 4 bytes per channel to get the next nibbles for this channel.
*
* First, decode the samples that we need to skip in the block (will
*/
auto decode_nibble = [&prevSample,&prevIndex,srcStep,nibbleData](const size_t nibbleOffset)
noexcept -> int
{
static constexpr auto NibbleMask = std::byte{0xf};
const auto byteShift = (nibbleOffset&1) * 4;
const auto wordOffset = (nibbleOffset>>1) & ~3_uz;
const auto byteOffset = wordOffset*srcStep + ((nibbleOffset>>1)&3);
const auto nibble = al::to_underlying((nibbleData[byteOffset]>>byteShift)&NibbleMask);
prevSample += IMA4Codeword[nibble] * IMAStep_size[static_cast<uint>(prevIndex)] / 8;
prevSample = std::clamp(prevSample, -32768, 32767);
prevIndex += IMA4Index_adjust[nibble];
prevIndex = std::clamp(prevIndex, 0, MaxStepIndex);
return prevSample;
};
/* First, decode the samples that we need to skip in the block (will
* always be less than the block size). They need to be decoded despite
* being ignored for proper state on the remaining samples.
*/
@ -350,29 +364,22 @@ inline void LoadSamples<FmtIMA4>(al::span<float> dstSamples, al::span<const std:
const size_t startOffset{skip + 1};
for(;skip;--skip)
{
const size_t byteShift{(nibbleOffset&1) * 4};
const size_t wordOffset{(nibbleOffset>>1) & ~3_uz};
const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)};
std::ignore = decode_nibble(nibbleOffset);
++nibbleOffset;
std::ignore = decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u);
}
/* Second, decode the rest of the block and write to the output, until
* the end of the block or the end of output.
*/
const size_t todo{std::min(samplesPerBlock-startOffset, dstSamples.size())};
std::generate_n(dstSamples.begin(), todo, [&]
const auto todo = std::min(samplesPerBlock - startOffset,
size_t(std::distance(dst, dstSamples.end())));
dst = std::generate_n(dst, todo, [&]
{
const size_t byteShift{(nibbleOffset&1) * 4};
const size_t wordOffset{(nibbleOffset>>1) & ~3_uz};
const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)};
const auto sample = decode_nibble(nibbleOffset);
++nibbleOffset;
const int result{decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u)};
return static_cast<float>(result) / 32768.0f;
return static_cast<float>(sample) / 32768.0f;
});
dstSamples = dstSamples.subspan(todo);
}
}
@ -389,30 +396,27 @@ inline void LoadSamples<FmtMSADPCM>(al::span<float> dstSamples, al::span<const s
src = src.subspan(srcOffset/samplesPerBlock*blockBytes);
size_t skip{srcOffset % samplesPerBlock};
while(!dstSamples.empty())
auto dst = dstSamples.begin();
while(dst != dstSamples.end())
{
auto input = src.cbegin();
src = src.subspan(blockBytes);
/* Each MS ADPCM block starts with an 8-bit block predictor, used to
* dictate how the two sample history values are mixed with the decoded
* sample, and an initial signed 16-bit delta value which scales the
* sample, and an initial signed 16-bit scaling value which scales the
* nibble sample value. This is followed by the two initial 16-bit
* sample history values.
*/
const uint8_t blockpred{std::min(uint8_t(input[srcChan]), uint8_t{6})};
input += ptrdiff_t(srcStep);
int delta{int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1]) << 8)};
input += ptrdiff_t(srcStep*2);
const auto blockpred = std::min(uint8_t(src[srcChan]),
uint8_t{MSADPCMAdaptionCoeff.size()-1});
auto scale = int(src[srcStep + 2*srcChan + 0]) | (int(src[srcStep + 2*srcChan + 1]) << 8);
std::array<int,2> sampleHistory{};
sampleHistory[0] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8);
input += ptrdiff_t(srcStep*2);
sampleHistory[1] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8);
input += ptrdiff_t(srcStep*2);
auto sampleHistory = std::array{
int(src[3*srcStep + 2*srcChan + 0]) | (int(src[3*srcStep + 2*srcChan + 1])<<8),
int(src[5*srcStep + 2*srcChan + 0]) | (int(src[5*srcStep + 2*srcChan + 1])<<8)};
const auto nibbleData = src.subspan(7*srcStep);
src = src.subspan(blockBytes);
const al::span coeffs{MSADPCMAdaptionCoeff[blockpred]};
delta = (delta^0x8000) - 32768;
const auto coeffs = al::span{MSADPCMAdaptionCoeff[blockpred]};
scale = (scale^0x8000) - 32768;
sampleHistory[0] = (sampleHistory[0]^0x8000) - 32768;
sampleHistory[1] = (sampleHistory[1]^0x8000) - 32768;
@ -421,66 +425,66 @@ inline void LoadSamples<FmtMSADPCM>(al::span<float> dstSamples, al::span<const s
*/
if(skip == 0)
{
dstSamples[0] = static_cast<float>(sampleHistory[1]) / 32768.0f;
dstSamples = dstSamples.subspan<1>();
if(dstSamples.empty()) return;
dstSamples[0] = static_cast<float>(sampleHistory[0]) / 32768.0f;
dstSamples = dstSamples.subspan<1>();
if(dstSamples.empty()) return;
*dst = static_cast<float>(sampleHistory[1]) / 32768.0f;
if(++dst == dstSamples.end()) return;
*dst = static_cast<float>(sampleHistory[0]) / 32768.0f;
if(++dst == dstSamples.end()) return;
}
else if(skip == 1)
{
--skip;
dstSamples[0] = static_cast<float>(sampleHistory[0]) / 32768.0f;
dstSamples = dstSamples.subspan<1>();
if(dstSamples.empty()) return;
*dst = static_cast<float>(sampleHistory[0]) / 32768.0f;
if(++dst == dstSamples.end()) return;
}
else
skip -= 2;
auto decode_sample = [&sampleHistory,&delta,coeffs](const int nibble)
/* The rest of the block is a series of nibbles, interleaved per-
* channel.
*/
auto decode_nibble = [&sampleHistory,&scale,coeffs,nibbleData](const size_t nibbleOffset)
noexcept -> int
{
int pred{(sampleHistory[0]*coeffs[0] + sampleHistory[1]*coeffs[1]) / 256};
pred += ((nibble^0x08) - 0x08) * delta;
pred = std::clamp(pred, -32768, 32767);
static constexpr auto NibbleMask = std::byte{0xf};
const auto byteOffset = nibbleOffset>>1;
const auto byteShift = ((nibbleOffset&1)^1) * 4;
const auto nibble = al::to_underlying((nibbleData[byteOffset]>>byteShift)&NibbleMask);
const auto pred = ((nibble^0x08) - 0x08) * scale;
const auto diff = (sampleHistory[0]*coeffs[0] + sampleHistory[1]*coeffs[1]) / 256;
const auto sample = std::clamp(pred + diff, -32768, 32767);
sampleHistory[1] = sampleHistory[0];
sampleHistory[0] = pred;
sampleHistory[0] = sample;
delta = (MSADPCMAdaption[static_cast<uint>(nibble)] * delta) / 256;
delta = std::max(16, delta);
scale = MSADPCMAdaption[nibble] * scale / 256;
scale = std::max(16, scale);
return pred;
return sample;
};
/* The rest of the block is a series of nibbles, interleaved per-
* channel. First, skip samples.
*/
/* First, skip samples. */
const size_t startOffset{skip + 2};
size_t nibbleOffset{srcChan};
for(;skip;--skip)
{
const size_t byteOffset{nibbleOffset>>1};
const size_t byteShift{((nibbleOffset&1)^1) * 4};
std::ignore = decode_nibble(nibbleOffset);
nibbleOffset += srcStep;
std::ignore = decode_sample(int(input[byteOffset]>>byteShift) & 15);
}
/* Now decode the rest of the block, until the end of the block or the
* dst buffer is filled.
*/
const size_t todo{std::min(samplesPerBlock-startOffset, dstSamples.size())};
std::generate_n(dstSamples.begin(), todo, [&]
const auto todo = std::min(samplesPerBlock - startOffset,
size_t(std::distance(dst, dstSamples.end())));
dst = std::generate_n(dst, todo, [&]
{
const size_t byteOffset{nibbleOffset>>1};
const size_t byteShift{((nibbleOffset&1)^1) * 4};
const auto sample = decode_nibble(nibbleOffset);
nibbleOffset += srcStep;
const int sample{decode_sample(int(input[byteOffset]>>byteShift) & 15)};
return static_cast<float>(sample) / 32768.0f;
});
dstSamples = dstSamples.subspan(todo);
}
}
@ -692,25 +696,24 @@ void DoNfcMix(const al::span<const float> samples, al::span<FloatBufferLine> Out
static constexpr std::array<FilterProc,MaxAmbiOrder+1> NfcProcess{{
nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}};
auto CurrentGains = al::span{parms.Gains.Current}.subspan(0);
auto TargetGains = OutGains.subspan(0);
MixSamples(samples, OutBuffer.first(1), CurrentGains, TargetGains, Counter, OutPos);
MixSamples(samples, al::span{OutBuffer[0]}.subspan(OutPos), parms.Gains.Current[0],
OutGains[0], Counter);
OutBuffer = OutBuffer.subspan(1);
CurrentGains = CurrentGains.subspan(1);
TargetGains = TargetGains.subspan(1);
auto CurrentGains = al::span{parms.Gains.Current}.subspan(1);
auto TargetGains = OutGains.subspan(1);
const auto nfcsamples = al::span{Device->ExtraSampleData}.subspan(samples.size());
const auto nfcsamples = al::span{Device->ExtraSampleData}.first(samples.size());
size_t order{1};
while(const size_t chancount{Device->NumChannelsPerOrder[order]})
{
(parms.NFCtrlFilter.*NfcProcess[order])(samples, nfcsamples);
MixSamples(nfcsamples, OutBuffer.first(chancount), CurrentGains, TargetGains, Counter,
OutPos);
if(++order == MaxAmbiOrder+1)
break;
OutBuffer = OutBuffer.subspan(chancount);
CurrentGains = CurrentGains.subspan(chancount);
TargetGains = TargetGains.subspan(chancount);
if(++order == MaxAmbiOrder+1)
break;
}
}
@ -773,17 +776,9 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
/* Get the number of samples ahead of the current time that output
* should start at. Skip this update if it's beyond the output sample
* count.
*
* Round the start position to a multiple of 4, which some mixers want.
* This makes the start time accurate to 4 samples. This could be made
* sample-accurate by forcing non-SIMD functions on the first run.
*/
seconds::rep sampleOffset{duration_cast<seconds>(diff * Device->Frequency).count()};
sampleOffset = (sampleOffset+2) & ~seconds::rep{3};
if(sampleOffset >= SamplesToDo)
return;
OutPos = static_cast<uint>(sampleOffset);
OutPos = static_cast<uint>(round<seconds>(diff * Device->mSampleRate).count());
if(OutPos >= SamplesToDo) return;
}
/* Calculate the number of samples to mix, and the number of (resampled)
@ -854,7 +849,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
dataSize64 += ext + MaxResamplerEdge;
if(dataSize64 <= srcSizeMax)
return std::make_pair(dstBufferSize, static_cast<uint>(dataSize64));
return std::array{dstBufferSize, static_cast<uint>(dataSize64)};
/* If the source size got saturated, we can't fill the desired
* dst size. Figure out how many dst samples we can fill.
@ -869,7 +864,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
*/
dstBufferSize = static_cast<uint>(dataSize64) & ~3u;
}
return std::make_pair(dstBufferSize, srcSizeMax);
return std::array{dstBufferSize, srcSizeMax};
};
const auto [dstBufferSize, srcBufferSize] = calc_buffer_sizes(
samplesToLoad - samplesLoaded);
@ -1193,9 +1188,9 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
{
RingBuffer *ring{Context->mAsyncEvents.get()};
auto evt_vec = ring->getWriteVector();
if(evt_vec.first.len > 0)
if(evt_vec[0].len > 0)
{
auto &evt = InitAsyncEvent<AsyncBufferCompleteEvent>(evt_vec.first.buf);
auto &evt = InitAsyncEvent<AsyncBufferCompleteEvent>(evt_vec[0].buf);
evt.mId = SourceID;
evt.mCount = buffers_done;
ring->writeAdvance(1);
@ -1223,7 +1218,7 @@ void Voice::prepare(DeviceBase *device)
: ChannelsFromFmt(mFmtChannels, std::min(mAmbiOrder, device->mAmbiOrder))};
if(num_channels > device->MixerChannelsMax) UNLIKELY
{
ERR("Unexpected channel count: %u (limit: %zu, %s : %d)\n", num_channels,
ERR("Unexpected channel count: {} (limit: {}, {} : {})", num_channels,
device->MixerChannelsMax, NameFromFormat(mFmtChannels), mAmbiOrder);
num_channels = device->MixerChannelsMax;
}
@ -1298,7 +1293,7 @@ void Voice::prepare(DeviceBase *device)
* Note this isn't needed with UHJ output (UHJ2->B-Format->UHJ2 is
* identity, so don't mess with it).
*/
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->mSampleRate)};
for(auto &chandata : mChans)
{
chandata.mAmbiHFScale = 1.0f;
@ -1325,7 +1320,7 @@ void Voice::prepare(DeviceBase *device)
const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder,
device->m2DMixing);
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->mSampleRate)};
for(auto &chandata : mChans)
{
chandata.mAmbiHFScale = scales[*(OrderFromChan++)];

View file

@ -10,7 +10,6 @@
#include <optional>
#include <string>
#include "almalloc.h"
#include "alspan.h"
#include "bufferline.h"
#include "buffer_storage.h"
@ -20,6 +19,7 @@
#include "filters/splitter.h"
#include "mixer/defs.h"
#include "mixer/hrtfdefs.h"
#include "opthelpers.h"
#include "resampler_limits.h"
#include "uhjfilter.h"
#include "vector.h"
@ -102,7 +102,10 @@ struct VoiceBufferItem {
uint mLoopStart{0u};
uint mLoopEnd{0u};
al::span<std::byte> mSamples{};
al::span<std::byte> mSamples;
protected:
~VoiceBufferItem() = default;
};
@ -180,7 +183,7 @@ enum : uint {
VoiceFlagCount
};
struct Voice {
struct SIMDALIGN Voice {
enum State {
Stopped,
Playing,
@ -233,9 +236,9 @@ struct Voice {
ResamplerFunc mResampler{};
InterpState mResampleState{};
InterpState mResampleState;
std::bitset<VoiceFlagCount> mFlags{};
std::bitset<VoiceFlagCount> mFlags;
uint mNumCallbackBlocks{0};
uint mCallbackBlockBase{0};
@ -277,6 +280,6 @@ struct Voice {
static void InitMixer(std::optional<std::string> resopt);
};
inline Resampler ResamplerDefault{Resampler::Gaussian};
inline Resampler ResamplerDefault{Resampler::Spline};
#endif /* CORE_VOICE_H */