mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-15 00:24:40 +00:00
update openal-soft to 1.24.3
keeping the alt 87514151c4 (diff-73a8dc1ce58605f6c5ea53548454c3bae516ec5132a29c9d7ff7edf9730c75be)
This commit is contained in:
parent
12db0500e8
commit
ba32094b7b
276 changed files with 49304 additions and 8712 deletions
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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()};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)};
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <string>
|
||||
|
||||
|
||||
extern int CPUCapFlags;
|
||||
inline int CPUCapFlags{0};
|
||||
enum {
|
||||
CPU_CAP_SSE = 1<<0,
|
||||
CPU_CAP_SSE2 = 1<<1,
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@
|
|||
#include <cstddef>
|
||||
|
||||
#include "cubic_defs.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
struct CubicTable {
|
||||
struct SIMDALIGN CubicTable {
|
||||
std::array<CubicCoefficients,CubicPhaseCount> mTable{};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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; \
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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{};
|
||||
|
|
|
|||
|
|
@ -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, ¶m);
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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{};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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++)];
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue