aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/ambdec.cpp10
-rw-r--r--core/ambdec.h4
-rw-r--r--core/ambidefs.cpp236
-rw-r--r--core/ambidefs.h239
-rw-r--r--core/async_event.h92
-rw-r--r--core/bformatdec.cpp64
-rw-r--r--core/bformatdec.h24
-rw-r--r--core/bsinc_tables.cpp92
-rw-r--r--core/buffer_storage.cpp2
-rw-r--r--core/buffer_storage.h5
-rw-r--r--core/context.cpp11
-rw-r--r--core/context.h18
-rw-r--r--core/converter.cpp102
-rw-r--r--core/converter.h1
-rw-r--r--core/cpu_caps.cpp5
-rw-r--r--core/cpu_caps.h5
-rw-r--r--core/cubic_tables.cpp16
-rw-r--r--core/dbus_wrap.cpp8
-rw-r--r--core/dbus_wrap.h4
-rw-r--r--core/device.h34
-rw-r--r--core/effects/base.h7
-rw-r--r--core/filters/nfc.h2
-rw-r--r--core/fmt_traits.h28
-rw-r--r--core/fpu_ctrl.cpp71
-rw-r--r--core/helpers.cpp86
-rw-r--r--core/helpers.h14
-rw-r--r--core/hrtf.cpp70
-rw-r--r--core/hrtf.h6
-rw-r--r--core/logging.cpp106
-rw-r--r--core/logging.h33
-rw-r--r--core/mastering.cpp4
-rw-r--r--core/mixer.cpp7
-rw-r--r--core/mixer.h8
-rw-r--r--core/mixer/mixer_neon.cpp4
-rw-r--r--core/mixer/mixer_sse.cpp4
-rw-r--r--core/uhjfilter.cpp260
-rw-r--r--core/uhjfilter.h48
-rw-r--r--core/uiddefs.cpp6
-rw-r--r--core/voice.cpp73
-rw-r--r--core/voice.h9
40 files changed, 1080 insertions, 738 deletions
diff --git a/core/ambdec.cpp b/core/ambdec.cpp
index 8ca182c4..f98e1098 100644
--- a/core/ambdec.cpp
+++ b/core/ambdec.cpp
@@ -47,9 +47,9 @@ enum class ReaderScope {
#else
[[gnu::format(printf,2,3)]]
#endif
-al::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
+std::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
{
- al::optional<std::string> ret;
+ std::optional<std::string> ret;
auto &str = ret.emplace();
str.resize(256);
@@ -77,7 +77,7 @@ al::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
AmbDecConf::~AmbDecConf() = default;
-al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
+std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
{
al::ifstream f{fname};
if(!f.is_open())
@@ -139,7 +139,7 @@ al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
{
--toread;
istr >> value;
- if(curgain < al::size(gains))
+ if(curgain < std::size(gains))
gains[curgain++] = value;
}
}
@@ -291,7 +291,7 @@ al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
if(CoeffScale == AmbDecScale::Unset)
return make_error(linenum, "No coefficient scaling defined");
- return al::nullopt;
+ return std::nullopt;
}
else
return make_error(linenum, "Unexpected command: %s", command.c_str());
diff --git a/core/ambdec.h b/core/ambdec.h
index 7f739781..19f68697 100644
--- a/core/ambdec.h
+++ b/core/ambdec.h
@@ -3,9 +3,9 @@
#include <array>
#include <memory>
+#include <optional>
#include <string>
-#include "aloptional.h"
#include "core/ambidefs.h"
/* Helpers to read .ambdec configuration files. */
@@ -49,7 +49,7 @@ struct AmbDecConf {
~AmbDecConf();
- al::optional<std::string> load(const char *fname) noexcept;
+ std::optional<std::string> load(const char *fname) noexcept;
};
#endif /* CORE_AMBDEC_H */
diff --git a/core/ambidefs.cpp b/core/ambidefs.cpp
index 70d6f356..2389ce6b 100644
--- a/core/ambidefs.cpp
+++ b/core/ambidefs.cpp
@@ -21,25 +21,25 @@ constexpr auto inv_sqrt3f = static_cast<float>(1.0/al::numbers::sqrt3);
* will result in that channel being subsequently decoded for second-order as
* if it was a first-order decoder for that same speaker array.
*/
-constexpr std::array<std::array<float,MaxAmbiOrder+1>,MaxAmbiOrder+1> HFScales{{
- {{ 4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }},
- {{ 4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }},
- {{ 2.981423970e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }},
- {{ 2.359168820e+00f, 2.031565936e+00f, 1.444598386e+00f, 7.189495850e-01f }},
- /* 1.947005434e+00f, 1.764337084e+00f, 1.424707344e+00f, 9.755104127e-01f, 4.784482742e-01f */
-}};
+constexpr std::array HFScales{
+ std::array{4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f},
+ std::array{4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f},
+ std::array{2.981423970e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f},
+ std::array{2.359168820e+00f, 2.031565936e+00f, 1.444598386e+00f, 7.189495850e-01f},
+ /*std::array{1.947005434e+00f, 1.764337084e+00f, 1.424707344e+00f, 9.755104127e-01f, 4.784482742e-01f}, */
+};
/* Same as above, but using a 10-point horizontal-only speaker array. Should
* only be used when the device is mixing in 2D B-Format for horizontal-only
* output.
*/
-constexpr std::array<std::array<float,MaxAmbiOrder+1>,MaxAmbiOrder+1> HFScales2D{{
- {{ 2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }},
- {{ 2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }},
- {{ 1.825741858e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }},
- {{ 1.581138830e+00f, 1.460781803e+00f, 1.118033989e+00f, 6.050756345e-01f }},
- /* 1.414213562e+00f, 1.344997024e+00f, 1.144122806e+00f, 8.312538756e-01f, 4.370160244e-01f */
-}};
+constexpr std::array HFScales2D{
+ std::array{2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f},
+ std::array{2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f},
+ std::array{1.825741858e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f},
+ std::array{1.581138830e+00f, 1.460781803e+00f, 1.118033989e+00f, 6.050756345e-01f},
+ /*std::array{1.414213562e+00f, 1.344997024e+00f, 1.144122806e+00f, 8.312538756e-01f, 4.370160244e-01f}, */
+};
/* This calculates a first-order "upsampler" matrix. It combines a first-order
@@ -49,17 +49,17 @@ constexpr std::array<std::array<float,MaxAmbiOrder+1>,MaxAmbiOrder+1> HFScales2D
* signal. While not perfect, this should accurately encode a lower-order
* signal into a higher-order signal.
*/
-constexpr std::array<std::array<float,4>,8> FirstOrderDecoder{{
- {{ 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, }},
- {{ 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, }},
- {{ 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, }},
- {{ 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, }},
- {{ 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, }},
- {{ 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, }},
- {{ 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, }},
- {{ 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,8> FirstOrderEncoder{{
+constexpr std::array FirstOrderDecoder{
+ std::array{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f},
+ std::array{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f},
+ std::array{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f},
+ std::array{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f},
+ std::array{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f},
+ std::array{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f},
+ std::array{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f},
+ std::array{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f},
+};
+constexpr std::array FirstOrderEncoder{
CalcAmbiCoeffs( inv_sqrt3f, inv_sqrt3f, inv_sqrt3f),
CalcAmbiCoeffs( inv_sqrt3f, inv_sqrt3f, -inv_sqrt3f),
CalcAmbiCoeffs(-inv_sqrt3f, inv_sqrt3f, inv_sqrt3f),
@@ -68,25 +68,25 @@ constexpr std::array<AmbiChannelFloatArray,8> FirstOrderEncoder{{
CalcAmbiCoeffs( inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f),
CalcAmbiCoeffs(-inv_sqrt3f, -inv_sqrt3f, inv_sqrt3f),
CalcAmbiCoeffs(-inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f),
-}};
+};
static_assert(FirstOrderDecoder.size() == FirstOrderEncoder.size(), "First-order mismatch");
/* This calculates a 2D first-order "upsampler" matrix. Same as the first-order
* matrix, just using a more optimized speaker array for horizontal-only
* content.
*/
-constexpr std::array<std::array<float,4>,4> FirstOrder2DDecoder{{
- {{ 2.500000000e-01f, 2.041241452e-01f, 0.0f, 2.041241452e-01f, }},
- {{ 2.500000000e-01f, 2.041241452e-01f, 0.0f, -2.041241452e-01f, }},
- {{ 2.500000000e-01f, -2.041241452e-01f, 0.0f, 2.041241452e-01f, }},
- {{ 2.500000000e-01f, -2.041241452e-01f, 0.0f, -2.041241452e-01f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,4> FirstOrder2DEncoder{{
+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},
+};
+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),
-}};
+};
static_assert(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-order 2D mismatch");
@@ -94,21 +94,21 @@ static_assert(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-o
* matrix, just using a slightly more dense speaker array suitable for second-
* order content.
*/
-constexpr std::array<std::array<float,9>,12> SecondOrderDecoder{{
- {{ 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }},
- {{ 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }},
- {{ 8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }},
- {{ 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }},
- {{ 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }},
- {{ 8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }},
- {{ 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }},
- {{ 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }},
- {{ 8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }},
- {{ 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }},
- {{ 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }},
- {{ 8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,12> SecondOrderEncoder{{
+constexpr std::array SecondOrderDecoder{
+ std::array{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f},
+ std::array{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f},
+ std::array{8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f},
+ std::array{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f},
+ std::array{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f},
+ std::array{8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f},
+ std::array{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f},
+ std::array{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f},
+ std::array{8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f},
+ std::array{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f},
+ std::array{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f},
+ std::array{8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f},
+};
+constexpr std::array SecondOrderEncoder{
CalcAmbiCoeffs( 0.000000000e+00f, -5.257311121e-01f, 8.506508084e-01f),
CalcAmbiCoeffs(-8.506508084e-01f, 0.000000000e+00f, 5.257311121e-01f),
CalcAmbiCoeffs(-5.257311121e-01f, 8.506508084e-01f, 0.000000000e+00f),
@@ -121,29 +121,29 @@ constexpr std::array<AmbiChannelFloatArray,12> SecondOrderEncoder{{
CalcAmbiCoeffs( 0.000000000e+00f, 5.257311121e-01f, -8.506508084e-01f),
CalcAmbiCoeffs( 8.506508084e-01f, 0.000000000e+00f, 5.257311121e-01f),
CalcAmbiCoeffs(-5.257311121e-01f, -8.506508084e-01f, 0.000000000e+00f),
-}};
+};
static_assert(SecondOrderDecoder.size() == SecondOrderEncoder.size(), "Second-order mismatch");
/* This calculates a 2D second-order "upsampler" matrix. Same as the second-
* order matrix, just using a more optimized speaker array for horizontal-only
* content.
*/
-constexpr std::array<std::array<float,9>,6> SecondOrder2DDecoder{{
- {{ 1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }},
- {{ 1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f, }},
- {{ 1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }},
- {{ 1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }},
- {{ 1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f, }},
- {{ 1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,6> SecondOrder2DEncoder{{
+constexpr std::array SecondOrder2DDecoder{
+ std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f},
+ std::array{1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f},
+ std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f},
+ std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f},
+ std::array{1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f},
+ std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f},
+};
+constexpr std::array SecondOrder2DEncoder{
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(SecondOrder2DDecoder.size() == SecondOrder2DEncoder.size(),
"Second-order 2D mismatch");
@@ -152,29 +152,29 @@ static_assert(SecondOrder2DDecoder.size() == SecondOrder2DEncoder.size(),
* matrix, just using a more dense speaker array suitable for third-order
* content.
*/
-constexpr std::array<std::array<float,16>,20> ThirdOrderDecoder{{
- {{ 5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }},
- {{ 5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }},
- {{ 5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }},
- {{ 5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }},
- {{ 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }},
- {{ 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }},
- {{ 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }},
- {{ 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }},
- {{ 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f, }},
- {{ 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f, }},
- {{ 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f, }},
- {{ 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f, }},
- {{ 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }},
- {{ 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }},
- {{ 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }},
- {{ 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }},
- {{ 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }},
- {{ 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }},
- {{ 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }},
- {{ 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,20> ThirdOrderEncoder{{
+constexpr std::array ThirdOrderDecoder{
+ std::array{5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f},
+ std::array{5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f},
+ std::array{5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f},
+ std::array{5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f},
+ std::array{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f},
+ std::array{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f},
+ std::array{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f},
+ std::array{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f},
+ std::array{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f},
+ std::array{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f},
+ std::array{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f},
+ std::array{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f},
+ std::array{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f},
+ std::array{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f},
+ std::array{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f},
+ std::array{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f},
+ std::array{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f},
+ std::array{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f},
+ std::array{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f},
+ std::array{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f},
+};
+constexpr std::array ThirdOrderEncoder{
CalcAmbiCoeffs( 0.35682208976f, 0.93417235897f, 0.00000000000f),
CalcAmbiCoeffs(-0.35682208976f, 0.93417235897f, 0.00000000000f),
CalcAmbiCoeffs( 0.35682208976f, -0.93417235897f, 0.00000000000f),
@@ -195,24 +195,24 @@ constexpr std::array<AmbiChannelFloatArray,20> ThirdOrderEncoder{{
CalcAmbiCoeffs( inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f),
CalcAmbiCoeffs( -inv_sqrt3f, -inv_sqrt3f, inv_sqrt3f),
CalcAmbiCoeffs( -inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f),
-}};
+};
static_assert(ThirdOrderDecoder.size() == ThirdOrderEncoder.size(), "Third-order mismatch");
/* This calculates a 2D third-order "upsampler" matrix. Same as the third-order
* matrix, just using a more optimized speaker array for horizontal-only
* content.
*/
-constexpr std::array<std::array<float,16>,8> ThirdOrder2DDecoder{{
- {{ 1.250000000e-01f, -5.523559567e-02f, 0.0f, 1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f, }},
- {{ 1.250000000e-01f, -1.333505242e-01f, 0.0f, 5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f, }},
- {{ 1.250000000e-01f, -1.333505242e-01f, 0.0f, -5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f, }},
- {{ 1.250000000e-01f, -5.523559567e-02f, 0.0f, -1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f, }},
- {{ 1.250000000e-01f, 5.523559567e-02f, 0.0f, -1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f, }},
- {{ 1.250000000e-01f, 1.333505242e-01f, 0.0f, -5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f, }},
- {{ 1.250000000e-01f, 1.333505242e-01f, 0.0f, 5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f, }},
- {{ 1.250000000e-01f, 5.523559567e-02f, 0.0f, 1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,8> ThirdOrder2DEncoder{{
+constexpr std::array ThirdOrder2DDecoder{
+ std::array{1.250000000e-01f, -5.523559567e-02f, 0.0f, 1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f},
+ std::array{1.250000000e-01f, -1.333505242e-01f, 0.0f, 5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f},
+ std::array{1.250000000e-01f, -1.333505242e-01f, 0.0f, -5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f},
+ std::array{1.250000000e-01f, -5.523559567e-02f, 0.0f, -1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f},
+ std::array{1.250000000e-01f, 5.523559567e-02f, 0.0f, -1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f},
+ std::array{1.250000000e-01f, 1.333505242e-01f, 0.0f, -5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f},
+ std::array{1.250000000e-01f, 1.333505242e-01f, 0.0f, 5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f},
+ std::array{1.250000000e-01f, 5.523559567e-02f, 0.0f, 1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f},
+};
+constexpr std::array ThirdOrder2DEncoder{
CalcAmbiCoeffs(-0.38268343237f, 0.0f, 0.92387953251f),
CalcAmbiCoeffs(-0.92387953251f, 0.0f, 0.38268343237f),
CalcAmbiCoeffs(-0.92387953251f, 0.0f, -0.38268343237f),
@@ -221,7 +221,7 @@ constexpr std::array<AmbiChannelFloatArray,8> ThirdOrder2DEncoder{{
CalcAmbiCoeffs( 0.92387953251f, 0.0f, -0.38268343237f),
CalcAmbiCoeffs( 0.92387953251f, 0.0f, 0.38268343237f),
CalcAmbiCoeffs( 0.38268343237f, 0.0f, 0.92387953251f),
-}};
+};
static_assert(ThirdOrder2DDecoder.size() == ThirdOrder2DEncoder.size(), "Third-order 2D mismatch");
@@ -230,19 +230,19 @@ static_assert(ThirdOrder2DDecoder.size() == ThirdOrder2DEncoder.size(), "Third-o
* the foreseeable future. This is only necessary for mixing horizontal-only
* fourth-order content to 3D.
*/
-constexpr std::array<std::array<float,25>,10> FourthOrder2DDecoder{{
- {{ 1.000000000e-01f, 3.568220898e-02f, 0.0f, 1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }},
- {{ 1.000000000e-01f, 9.341723590e-02f, 0.0f, 6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }},
- {{ 1.000000000e-01f, 1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, -9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f, }},
- {{ 1.000000000e-01f, 9.341723590e-02f, 0.0f, -6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }},
- {{ 1.000000000e-01f, 3.568220898e-02f, 0.0f, -1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }},
- {{ 1.000000000e-01f, -3.568220898e-02f, 0.0f, -1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }},
- {{ 1.000000000e-01f, -9.341723590e-02f, 0.0f, -6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }},
- {{ 1.000000000e-01f, -1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, 9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f, }},
- {{ 1.000000000e-01f, -9.341723590e-02f, 0.0f, 6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }},
- {{ 1.000000000e-01f, -3.568220898e-02f, 0.0f, 1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,10> FourthOrder2DEncoder{{
+constexpr std::array FourthOrder2DDecoder{
+ std::array{1.000000000e-01f, 3.568220898e-02f, 0.0f, 1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f},
+ std::array{1.000000000e-01f, 9.341723590e-02f, 0.0f, 6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f},
+ std::array{1.000000000e-01f, 1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, -9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f},
+ std::array{1.000000000e-01f, 9.341723590e-02f, 0.0f, -6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f},
+ std::array{1.000000000e-01f, 3.568220898e-02f, 0.0f, -1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f},
+ std::array{1.000000000e-01f, -3.568220898e-02f, 0.0f, -1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f},
+ std::array{1.000000000e-01f, -9.341723590e-02f, 0.0f, -6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f},
+ std::array{1.000000000e-01f, -1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, 9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f},
+ std::array{1.000000000e-01f, -9.341723590e-02f, 0.0f, 6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f},
+ std::array{1.000000000e-01f, -3.568220898e-02f, 0.0f, 1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f},
+};
+constexpr std::array FourthOrder2DEncoder{
CalcAmbiCoeffs( 3.090169944e-01f, 0.000000000e+00f, 9.510565163e-01f),
CalcAmbiCoeffs( 8.090169944e-01f, 0.000000000e+00f, 5.877852523e-01f),
CalcAmbiCoeffs( 1.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f),
@@ -253,12 +253,12 @@ constexpr std::array<AmbiChannelFloatArray,10> FourthOrder2DEncoder{{
CalcAmbiCoeffs(-1.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f),
CalcAmbiCoeffs(-8.090169944e-01f, 0.000000000e+00f, 5.877852523e-01f),
CalcAmbiCoeffs(-3.090169944e-01f, 0.000000000e+00f, 9.510565163e-01f),
-}};
+};
static_assert(FourthOrder2DDecoder.size() == FourthOrder2DEncoder.size(), "Fourth-order 2D mismatch");
template<size_t N, size_t M>
-auto CalcAmbiUpsampler(const std::array<std::array<float,N>,M> &decoder,
+constexpr auto CalcAmbiUpsampler(const std::array<std::array<float,N>,M> &decoder,
const std::array<AmbiChannelFloatArray,M> &encoder)
{
std::array<AmbiChannelFloatArray,N> res{};
@@ -279,13 +279,13 @@ auto CalcAmbiUpsampler(const std::array<std::array<float,N>,M> &decoder,
} // namespace
-const std::array<AmbiChannelFloatArray,4> AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)};
-const std::array<AmbiChannelFloatArray,4> AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)};
-const std::array<AmbiChannelFloatArray,9> AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)};
-const std::array<AmbiChannelFloatArray,9> AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)};
-const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)};
-const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)};
-const std::array<AmbiChannelFloatArray,25> AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,4> AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,4> AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,9> AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,9> AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,16> AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,16> AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,25> AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)};
std::array<float,MaxAmbiOrder+1> AmbiScale::GetHFOrderScales(const uint src_order,
diff --git a/core/ambidefs.h b/core/ambidefs.h
index b7d2bcd1..bea1a312 100644
--- a/core/ambidefs.h
+++ b/core/ambidefs.h
@@ -14,26 +14,26 @@ 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.
*/
-constexpr uint8_t MaxAmbiOrder{3};
+inline constexpr uint8_t MaxAmbiOrder{3};
constexpr inline size_t AmbiChannelsFromOrder(size_t order) noexcept
{ return (order+1) * (order+1); }
-constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)};
+inline constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)};
/* A bitmask of ambisonic channels for 0 to 4th order. This only specifies up
* to 4th order, which is the highest order a 32-bit mask value can specify (a
* 64-bit mask could handle up to 7th order).
*/
-constexpr uint Ambi0OrderMask{0x00000001};
-constexpr uint Ambi1OrderMask{0x0000000f};
-constexpr uint Ambi2OrderMask{0x000001ff};
-constexpr uint Ambi3OrderMask{0x0000ffff};
-constexpr uint Ambi4OrderMask{0x01ffffff};
+inline constexpr uint Ambi0OrderMask{0x00000001};
+inline constexpr uint Ambi1OrderMask{0x0000000f};
+inline constexpr uint Ambi2OrderMask{0x000001ff};
+inline constexpr uint Ambi3OrderMask{0x0000ffff};
+inline constexpr uint Ambi4OrderMask{0x01ffffff};
/* A bitmask of ambisonic channels with height information. If none of these
* channels are used/needed, there's no height (e.g. with most surround sound
* speaker setups). This is ACN ordering, with bit 0 being ACN 0, etc.
*/
-constexpr uint AmbiPeriphonicMask{0xfe7ce4};
+inline constexpr uint AmbiPeriphonicMask{0xfe7ce4};
/* The maximum number of ambisonic channels for 2D (non-periphonic)
* representation. This is 2 per each order above zero-order, plus 1 for zero-
@@ -41,77 +41,61 @@ constexpr uint AmbiPeriphonicMask{0xfe7ce4};
*/
constexpr inline size_t Ambi2DChannelsFromOrder(size_t order) noexcept
{ return order*2 + 1; }
-constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)};
+inline constexpr size_t 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 auto& FromN3D() noexcept
- {
- static constexpr const std::array<float,MaxAmbiChannels> ret{{
- 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
- }};
- return ret;
- }
- static auto& FromSN3D() noexcept
- {
- static constexpr const std::array<float,MaxAmbiChannels> ret{{
- 1.000000000f, /* ACN 0, sqrt(1) */
- 1.732050808f, /* ACN 1, sqrt(3) */
- 1.732050808f, /* ACN 2, sqrt(3) */
- 1.732050808f, /* ACN 3, sqrt(3) */
- 2.236067978f, /* ACN 4, sqrt(5) */
- 2.236067978f, /* ACN 5, sqrt(5) */
- 2.236067978f, /* ACN 6, sqrt(5) */
- 2.236067978f, /* ACN 7, sqrt(5) */
- 2.236067978f, /* ACN 8, sqrt(5) */
- 2.645751311f, /* ACN 9, sqrt(7) */
- 2.645751311f, /* ACN 10, sqrt(7) */
- 2.645751311f, /* ACN 11, sqrt(7) */
- 2.645751311f, /* ACN 12, sqrt(7) */
- 2.645751311f, /* ACN 13, sqrt(7) */
- 2.645751311f, /* ACN 14, sqrt(7) */
- 2.645751311f, /* ACN 15, sqrt(7) */
- }};
- return ret;
- }
- static auto& FromFuMa() noexcept
- {
- static constexpr const std::array<float,MaxAmbiChannels> ret{{
- 1.414213562f, /* ACN 0 (W), sqrt(2) */
- 1.732050808f, /* ACN 1 (Y), sqrt(3) */
- 1.732050808f, /* ACN 2 (Z), sqrt(3) */
- 1.732050808f, /* ACN 3 (X), sqrt(3) */
- 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
- 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
- 2.236067978f, /* ACN 6 (R), sqrt(5) */
- 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
- 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
- 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
- 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
- 2.231093404f, /* ACN 11 (M), sqrt(224/45) */
- 2.645751311f, /* ACN 12 (K), sqrt(7) */
- 2.231093404f, /* ACN 13 (L), sqrt(224/45) */
- 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
- 2.091650066f, /* ACN 15 (P), sqrt(35/8) */
- }};
- return ret;
- }
- static auto& FromUHJ() noexcept
- {
- static constexpr const std::array<float,MaxAmbiChannels> ret{{
- 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,
- }};
- return ret;
- }
+ static inline constexpr std::array<float,MaxAmbiChannels> FromN3D{{
+ 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{{
+ 1.000000000f, /* ACN 0, sqrt(1) */
+ 1.732050808f, /* ACN 1, sqrt(3) */
+ 1.732050808f, /* ACN 2, sqrt(3) */
+ 1.732050808f, /* ACN 3, sqrt(3) */
+ 2.236067978f, /* ACN 4, sqrt(5) */
+ 2.236067978f, /* ACN 5, sqrt(5) */
+ 2.236067978f, /* ACN 6, sqrt(5) */
+ 2.236067978f, /* ACN 7, sqrt(5) */
+ 2.236067978f, /* ACN 8, sqrt(5) */
+ 2.645751311f, /* ACN 9, sqrt(7) */
+ 2.645751311f, /* ACN 10, sqrt(7) */
+ 2.645751311f, /* ACN 11, sqrt(7) */
+ 2.645751311f, /* ACN 12, sqrt(7) */
+ 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{{
+ 1.414213562f, /* ACN 0 (W), sqrt(2) */
+ 1.732050808f, /* ACN 1 (Y), sqrt(3) */
+ 1.732050808f, /* ACN 2 (Z), sqrt(3) */
+ 1.732050808f, /* ACN 3 (X), sqrt(3) */
+ 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
+ 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
+ 2.236067978f, /* ACN 6 (R), sqrt(5) */
+ 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
+ 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
+ 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
+ 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
+ 2.231093404f, /* ACN 11 (M), sqrt(224/45) */
+ 2.645751311f, /* ACN 12 (K), sqrt(7) */
+ 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{{
+ 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,
@@ -127,72 +111,49 @@ struct AmbiScale {
};
struct AmbiIndex {
- static auto& FromFuMa() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbiChannels> ret{{
- 0, /* W */
- 3, /* X */
- 1, /* Y */
- 2, /* Z */
- 6, /* R */
- 7, /* S */
- 5, /* T */
- 8, /* U */
- 4, /* V */
- 12, /* K */
- 13, /* L */
- 11, /* M */
- 14, /* N */
- 10, /* O */
- 15, /* P */
- 9, /* Q */
- }};
- return ret;
- }
- static auto& FromFuMa2D() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbi2DChannels> ret{{
- 0, /* W */
- 3, /* X */
- 1, /* Y */
- 8, /* U */
- 4, /* V */
- 15, /* P */
- 9, /* Q */
- }};
- return ret;
- }
-
- static auto& FromACN() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbiChannels> ret{{
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15
- }};
- return ret;
- }
- static auto& FromACN2D() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbi2DChannels> ret{{
- 0, 1,3, 4,8, 9,15
- }};
- return ret;
- }
-
- static auto& OrderFromChannel() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbiChannels> ret{{
- 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3,
- }};
- return ret;
- }
- static auto& OrderFrom2DChannel() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbi2DChannels> ret{{
- 0, 1,1, 2,2, 3,3,
- }};
- return ret;
- }
+ static inline constexpr std::array<uint8_t,MaxAmbiChannels> FromFuMa{{
+ 0, /* W */
+ 3, /* X */
+ 1, /* Y */
+ 2, /* Z */
+ 6, /* R */
+ 7, /* S */
+ 5, /* T */
+ 8, /* U */
+ 4, /* V */
+ 12, /* K */
+ 13, /* L */
+ 11, /* M */
+ 14, /* N */
+ 10, /* O */
+ 15, /* P */
+ 9, /* Q */
+ }};
+ static inline constexpr std::array<uint8_t,MaxAmbi2DChannels> FromFuMa2D{{
+ 0, /* W */
+ 3, /* X */
+ 1, /* Y */
+ 8, /* U */
+ 4, /* V */
+ 15, /* P */
+ 9, /* Q */
+ }};
+
+ static inline constexpr std::array<uint8_t,MaxAmbiChannels> FromACN{{
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15
+ }};
+ static inline constexpr std::array<uint8_t,MaxAmbi2DChannels> FromACN2D{{
+ 0, 1,3, 4,8, 9,15
+ }};
+
+
+ static inline constexpr std::array<uint8_t,MaxAmbiChannels> OrderFromChannel{{
+ 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3,
+ }};
+ static inline constexpr std::array<uint8_t,MaxAmbi2DChannels> OrderFrom2DChannel{{
+ 0, 1,1, 2,2, 3,3,
+ }};
};
diff --git a/core/async_event.h b/core/async_event.h
index 5a2f5f91..f1ca0c7b 100644
--- a/core/async_event.h
+++ b/core/async_event.h
@@ -1,6 +1,9 @@
#ifndef CORE_EVENT_H
#define CORE_EVENT_H
+#include <stdint.h>
+#include <variant>
+
#include "almalloc.h"
struct EffectState;
@@ -8,48 +11,53 @@ struct EffectState;
using uint = unsigned int;
-struct AsyncEvent {
- enum : uint {
- /* User event types. */
- SourceStateChange,
- BufferCompleted,
- Disconnected,
- UserEventCount,
-
- /* Internal events, always processed. */
- ReleaseEffectState = 128,
-
- /* End event thread processing. */
- KillThread,
- };
-
- enum class SrcState {
- Reset,
- Stop,
- Play,
- Pause
- };
-
- const uint EnumType;
- union {
- char dummy;
- struct {
- uint id;
- SrcState state;
- } srcstate;
- struct {
- uint id;
- uint count;
- } bufcomp;
- struct {
- char msg[244];
- } disconnect;
- EffectState *mEffectState;
- } u{};
-
- constexpr AsyncEvent(uint type) noexcept : EnumType{type} { }
-
- DISABLE_ALLOC()
+enum class AsyncEnableBits : uint8_t {
+ SourceState,
+ BufferCompleted,
+ Disconnected,
+ Count
+};
+
+
+enum class AsyncSrcState : uint8_t {
+ Reset,
+ Stop,
+ Play,
+ Pause
+};
+
+using AsyncKillThread = std::monostate;
+
+struct AsyncSourceStateEvent {
+ uint mId;
+ AsyncSrcState mState;
};
+struct AsyncBufferCompleteEvent {
+ uint mId;
+ uint mCount;
+};
+
+struct AsyncDisconnectEvent {
+ char msg[244];
+};
+
+struct AsyncEffectReleaseEvent {
+ EffectState *mEffectState;
+};
+
+using AsyncEvent = std::variant<AsyncKillThread,
+ AsyncSourceStateEvent,
+ AsyncBufferCompleteEvent,
+ AsyncEffectReleaseEvent,
+ AsyncDisconnectEvent>;
+
+template<typename T, typename ...Args>
+auto &InitAsyncEvent(std::byte *evtbuf, Args&& ...args)
+{
+ auto *evt = al::construct_at(reinterpret_cast<AsyncEvent*>(evtbuf), std::in_place_type<T>,
+ std::forward<Args>(args)...);
+ return std::get<T>(*evt);
+}
+
#endif
diff --git a/core/bformatdec.cpp b/core/bformatdec.cpp
index 129b9976..a308e185 100644
--- a/core/bformatdec.cpp
+++ b/core/bformatdec.cpp
@@ -16,33 +16,45 @@
#include "opthelpers.h"
+namespace {
+
+template<typename... Ts>
+struct overloaded : Ts... { using Ts::operator()...; };
+
+template<typename... Ts>
+overloaded(Ts...) -> overloaded<Ts...>;
+
+} // namespace
+
BFormatDec::BFormatDec(const size_t inchans, const al::span<const ChannelDec> coeffs,
const al::span<const ChannelDec> coeffslf, const float xover_f0norm,
std::unique_ptr<FrontStablizer> stablizer)
- : mStablizer{std::move(stablizer)}, mDualBand{!coeffslf.empty()}, mChannelDec{inchans}
+ : mStablizer{std::move(stablizer)}
{
- if(!mDualBand)
+ if(coeffslf.empty())
{
- for(size_t j{0};j < mChannelDec.size();++j)
+ auto &decoder = mChannelDec.emplace<std::vector<ChannelDecoderSingle>>(inchans);
+ for(size_t j{0};j < decoder.size();++j)
{
- float *outcoeffs{mChannelDec[j].mGains.Single};
+ float *outcoeffs{decoder[j].mGains};
for(const ChannelDec &incoeffs : coeffs)
*(outcoeffs++) = incoeffs[j];
}
}
else
{
- mChannelDec[0].mXOver.init(xover_f0norm);
- for(size_t j{1};j < mChannelDec.size();++j)
- mChannelDec[j].mXOver = mChannelDec[0].mXOver;
+ auto &decoder = mChannelDec.emplace<std::vector<ChannelDecoderDual>>(inchans);
+ decoder[0].mXOver.init(xover_f0norm);
+ for(size_t j{1};j < decoder.size();++j)
+ decoder[j].mXOver = decoder[0].mXOver;
- for(size_t j{0};j < mChannelDec.size();++j)
+ for(size_t j{0};j < decoder.size();++j)
{
- float *outcoeffs{mChannelDec[j].mGains.Dual[sHFBand]};
+ float *outcoeffs{decoder[j].mGains[sHFBand]};
for(const ChannelDec &incoeffs : coeffs)
*(outcoeffs++) = incoeffs[j];
- outcoeffs = mChannelDec[j].mGains.Dual[sLFBand];
+ outcoeffs = decoder[j].mGains[sLFBand];
for(const ChannelDec &incoeffs : coeffslf)
*(outcoeffs++) = incoeffs[j];
}
@@ -55,30 +67,32 @@ void BFormatDec::process(const al::span<FloatBufferLine> OutBuffer,
{
ASSUME(SamplesToDo > 0);
- if(mDualBand)
+ auto decode_dualband = [=](std::vector<ChannelDecoderDual> &decoder)
{
+ auto *input = InSamples;
const al::span<float> hfSamples{mSamples[sHFBand].data(), SamplesToDo};
const al::span<float> lfSamples{mSamples[sLFBand].data(), SamplesToDo};
- for(auto &chandec : mChannelDec)
+ for(auto &chandec : decoder)
{
- chandec.mXOver.process({InSamples->data(), SamplesToDo}, hfSamples.data(),
+ chandec.mXOver.process({input->data(), SamplesToDo}, hfSamples.data(),
lfSamples.data());
- MixSamples(hfSamples, OutBuffer, chandec.mGains.Dual[sHFBand],
- chandec.mGains.Dual[sHFBand], 0, 0);
- MixSamples(lfSamples, OutBuffer, chandec.mGains.Dual[sLFBand],
- chandec.mGains.Dual[sLFBand], 0, 0);
- ++InSamples;
+ MixSamples(hfSamples, OutBuffer, chandec.mGains[sHFBand], chandec.mGains[sHFBand],0,0);
+ MixSamples(lfSamples, OutBuffer, chandec.mGains[sLFBand], chandec.mGains[sLFBand],0,0);
+ ++input;
}
- }
- else
+ };
+ auto decode_singleband = [=](std::vector<ChannelDecoderSingle> &decoder)
{
- for(auto &chandec : mChannelDec)
+ auto *input = InSamples;
+ for(auto &chandec : decoder)
{
- MixSamples({InSamples->data(), SamplesToDo}, OutBuffer, chandec.mGains.Single,
- chandec.mGains.Single, 0, 0);
- ++InSamples;
+ MixSamples({input->data(), SamplesToDo}, OutBuffer, chandec.mGains, chandec.mGains,
+ 0, 0);
+ ++input;
}
- }
+ };
+
+ std::visit(overloaded{decode_dualband, decode_singleband}, mChannelDec);
}
void BFormatDec::processStablize(const al::span<FloatBufferLine> OutBuffer,
diff --git a/core/bformatdec.h b/core/bformatdec.h
index 7a27a5a4..3bb7f544 100644
--- a/core/bformatdec.h
+++ b/core/bformatdec.h
@@ -4,6 +4,8 @@
#include <array>
#include <cstddef>
#include <memory>
+#include <variant>
+#include <vector>
#include "almalloc.h"
#include "alspan.h"
@@ -11,7 +13,6 @@
#include "bufferline.h"
#include "devformat.h"
#include "filters/splitter.h"
-#include "vector.h"
struct FrontStablizer;
@@ -23,27 +24,20 @@ class BFormatDec {
static constexpr size_t sLFBand{1};
static constexpr size_t sNumBands{2};
- struct ChannelDecoder {
- union MatrixU {
- float Dual[sNumBands][MAX_OUTPUT_CHANNELS];
- float Single[MAX_OUTPUT_CHANNELS];
- } mGains{};
+ struct ChannelDecoderSingle {
+ float mGains[MAX_OUTPUT_CHANNELS];
+ };
- /* NOTE: BandSplitter filter is unused with single-band decoding. */
+ struct ChannelDecoderDual {
BandSplitter mXOver;
+ float mGains[sNumBands][MAX_OUTPUT_CHANNELS];
};
alignas(16) std::array<FloatBufferLine,2> mSamples;
const std::unique_ptr<FrontStablizer> mStablizer;
- const bool mDualBand{false};
-
- /* TODO: This should ideally be a FlexArray, since ChannelDecoder is rather
- * small and only a few are needed (3, 4, 5, 7, typically). But that can
- * only be used in a standard layout struct, and a std::unique_ptr member
- * (mStablizer) causes GCC and Clang to warn it's not.
- */
- al::vector<ChannelDecoder> mChannelDec;
+
+ std::variant<std::vector<ChannelDecoderSingle>,std::vector<ChannelDecoderDual>> mChannelDec;
public:
BFormatDec(const size_t inchans, const al::span<const ChannelDec> coeffs,
diff --git a/core/bsinc_tables.cpp b/core/bsinc_tables.cpp
index 693645f4..41102e9a 100644
--- a/core/bsinc_tables.cpp
+++ b/core/bsinc_tables.cpp
@@ -7,48 +7,50 @@
#include <cmath>
#include <limits>
#include <memory>
+#include <stddef.h>
#include <stdexcept>
#include "alnumbers.h"
-#include "core/mixer/defs.h"
+#include "alnumeric.h"
+#include "bsinc_defs.h"
+#include "resampler_limits.h"
namespace {
using uint = unsigned int;
+#if __cpp_lib_math_special_functions >= 201603L
+using std::cyl_bessel_i;
-/* This is the normalized cardinal sine (sinc) function.
- *
- * sinc(x) = { 1, x = 0
- * { sin(pi x) / (pi x), otherwise.
- */
-constexpr double Sinc(const double x)
-{
- constexpr double epsilon{std::numeric_limits<double>::epsilon()};
- if(!(x > epsilon || x < -epsilon))
- return 1.0;
- return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
-}
+#else
/* The zero-order modified Bessel function of the first kind, used for the
* Kaiser window.
*
* I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
* = sum_{k=0}^inf ((x / 2)^k / k!)^2
+ *
+ * This implementation only handles nu = 0, and isn't the most precise (it
+ * starts with the largest value and accumulates successively smaller values,
+ * compounding the rounding and precision error), but it's good enough.
*/
-constexpr double BesselI_0(const double x) noexcept
+template<typename T, typename U>
+U cyl_bessel_i(T nu, U x)
{
+ if(nu != T{0})
+ throw std::runtime_error{"cyl_bessel_i: nu != 0"};
+
/* Start at k=1 since k=0 is trivial. */
- const double x2{x / 2.0};
+ const double x2{x/2.0};
double term{1.0};
double sum{1.0};
- double last_sum{};
int k{1};
/* Let the integration converge until the term of the sum is no longer
* significant.
*/
+ double last_sum{};
do {
const double y{x2 / k};
++k;
@@ -56,8 +58,21 @@ constexpr double BesselI_0(const double x) noexcept
term *= y * y;
sum += term;
} while(sum != last_sum);
+ return static_cast<U>(sum);
+}
+#endif
- return sum;
+/* This is the normalized cardinal sine (sinc) function.
+ *
+ * sinc(x) = { 1, x = 0
+ * { sin(pi x) / (pi x), otherwise.
+ */
+constexpr double Sinc(const double x)
+{
+ constexpr double epsilon{std::numeric_limits<double>::epsilon()};
+ if(!(x > epsilon || x < -epsilon))
+ return 1.0;
+ return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
}
/* Calculate a Kaiser window from the given beta value and a normalized k
@@ -78,7 +93,7 @@ constexpr double Kaiser(const double beta, const double k, const double besseli_
{
if(!(k >= -1.0 && k <= 1.0))
return 0.0;
- return BesselI_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.
@@ -107,8 +122,6 @@ struct BSincHeader {
double width{};
double beta{};
double scaleBase{};
- double scaleRange{};
- double besseli_0_beta{};
uint a[BSincScaleCount]{};
uint total_size{};
@@ -118,13 +131,11 @@ struct BSincHeader {
width = CalcKaiserWidth(Rejection, Order);
beta = CalcKaiserBeta(Rejection);
scaleBase = width / 2.0;
- scaleRange = 1.0 - scaleBase;
- besseli_0_beta = BesselI_0(beta);
uint num_points{Order+1};
for(uint si{0};si < BSincScaleCount;++si)
{
- const double scale{scaleBase + (scaleRange * (si+1) / BSincScaleCount)};
+ 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_};
@@ -142,26 +153,6 @@ constexpr BSincHeader bsinc12_hdr{60, 11};
constexpr BSincHeader bsinc24_hdr{60, 23};
-/* NOTE: GCC 5 has an issue with BSincHeader objects being in an anonymous
- * namespace while also being used as non-type template parameters.
- */
-#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6
-
-/* The number of sample points is double the a value (rounded up to a multiple
- * of 4), and scale index 0 includes the doubling for downsampling. bsinc24 is
- * currently the highest quality filter, and will use the most sample points.
- */
-constexpr uint BSincPointsMax{(bsinc24_hdr.a[0]*2 + 3) & ~3u};
-static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small");
-
-template<size_t total_size>
-struct BSincFilterArray {
- alignas(16) std::array<float, total_size> mTable;
- const BSincHeader &hdr;
-
- BSincFilterArray(const BSincHeader &hdr_) : hdr{hdr_}
- {
-#else
template<const BSincHeader &hdr>
struct BSincFilterArray {
alignas(16) std::array<float, hdr.total_size> mTable{};
@@ -170,10 +161,12 @@ struct BSincFilterArray {
{
constexpr uint BSincPointsMax{(hdr.a[0]*2 + 3) & ~3u};
static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small");
-#endif
+
using filter_type = double[BSincPhaseCount+1][BSincPointsMax];
auto filter = std::make_unique<filter_type[]>(BSincScaleCount);
+ const double besseli_0_beta{cyl_bessel_i(0, hdr.beta)};
+
/* Calculate the Kaiser-windowed Sinc filter coefficients for each
* scale and phase index.
*/
@@ -181,7 +174,7 @@ struct BSincFilterArray {
{
const uint m{hdr.a[si] * 2};
const size_t o{(BSincPointsMax-m) / 2};
- const double scale{hdr.scaleBase + (hdr.scaleRange * (si+1) / BSincScaleCount)};
+ 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};
@@ -196,7 +189,7 @@ struct BSincFilterArray {
for(uint i{0};i < m;++i)
{
const double x{i - phase};
- filter[si][pi][o+i] = Kaiser(hdr.beta, x/l, hdr.besseli_0_beta) * cutoff *
+ filter[si][pi][o+i] = Kaiser(hdr.beta, x/l, besseli_0_beta) * cutoff *
Sinc(cutoff*x);
}
}
@@ -265,13 +258,8 @@ struct BSincFilterArray {
constexpr const float *getTable() const noexcept { return &mTable.front(); }
};
-#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6
-const BSincFilterArray<bsinc12_hdr.total_size> bsinc12_filter{bsinc12_hdr};
-const BSincFilterArray<bsinc24_hdr.total_size> bsinc24_filter{bsinc24_hdr};
-#else
const BSincFilterArray<bsinc12_hdr> bsinc12_filter{};
const BSincFilterArray<bsinc24_hdr> bsinc24_filter{};
-#endif
template<typename T>
constexpr BSincTable GenerateBSincTable(const T &filter)
@@ -279,7 +267,7 @@ constexpr BSincTable GenerateBSincTable(const T &filter)
BSincTable ret{};
const BSincHeader &hdr = filter.getHeader();
ret.scaleBase = static_cast<float>(hdr.scaleBase);
- ret.scaleRange = static_cast<float>(1.0 / hdr.scaleRange);
+ 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.filterOffset[0] = 0;
diff --git a/core/buffer_storage.cpp b/core/buffer_storage.cpp
index 98ca2c1b..6ffab124 100644
--- a/core/buffer_storage.cpp
+++ b/core/buffer_storage.cpp
@@ -12,6 +12,7 @@ const char *NameFromFormat(FmtType type) noexcept
{
case FmtUByte: return "UInt8";
case FmtShort: return "Int16";
+ case FmtInt: return "Int32";
case FmtFloat: return "Float";
case FmtDouble: return "Double";
case FmtMulaw: return "muLaw";
@@ -49,6 +50,7 @@ uint BytesFromFmt(FmtType type) noexcept
{
case FmtUByte: return sizeof(uint8_t);
case FmtShort: return sizeof(int16_t);
+ case FmtInt: return sizeof(int32_t);
case FmtFloat: return sizeof(float);
case FmtDouble: return sizeof(double);
case FmtMulaw: return sizeof(uint8_t);
diff --git a/core/buffer_storage.h b/core/buffer_storage.h
index 282d5b53..3b581b5e 100644
--- a/core/buffer_storage.h
+++ b/core/buffer_storage.h
@@ -2,8 +2,8 @@
#define CORE_BUFFER_STORAGE_H
#include <atomic>
+#include <cstddef>
-#include "albyte.h"
#include "alnumeric.h"
#include "alspan.h"
#include "ambidefs.h"
@@ -15,6 +15,7 @@ using uint = unsigned int;
enum FmtType : unsigned char {
FmtUByte,
FmtShort,
+ FmtInt,
FmtFloat,
FmtDouble,
FmtMulaw,
@@ -85,7 +86,7 @@ struct BufferStorage {
CallbackType mCallback{nullptr};
void *mUserData{nullptr};
- al::span<al::byte> mData;
+ al::span<std::byte> mData;
uint mSampleRate{0u};
FmtChannels mChannels{FmtMono};
diff --git a/core/context.cpp b/core/context.cpp
index d68d8327..2ebbc7b1 100644
--- a/core/context.cpp
+++ b/core/context.cpp
@@ -2,7 +2,10 @@
#include "config.h"
#include <cassert>
+#include <limits>
#include <memory>
+#include <stdexcept>
+#include <utility>
#include "async_event.h"
#include "context.h"
@@ -51,7 +54,7 @@ ContextBase::~ContextBase()
if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)})
{
- al::destroy_n(curarray->end(), curarray->size());
+ std::destroy_n(curarray->end(), curarray->size());
delete curarray;
}
@@ -63,12 +66,14 @@ ContextBase::~ContextBase()
auto evt_vec = mAsyncEvents->getReadVector();
if(evt_vec.first.len > 0)
{
- al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), evt_vec.first.len);
+ 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)
{
- al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf), evt_vec.second.len);
+ std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf)),
+ evt_vec.second.len);
count += evt_vec.second.len;
}
if(count > 0)
diff --git a/core/context.h b/core/context.h
index 9723eac3..ccb7dd3b 100644
--- a/core/context.h
+++ b/core/context.h
@@ -7,15 +7,15 @@
#include <cstddef>
#include <memory>
#include <thread>
+#include <vector>
#include "almalloc.h"
+#include "alsem.h"
#include "alspan.h"
#include "async_event.h"
#include "atomic.h"
-#include "bufferline.h"
-#include "threads.h"
+#include "opthelpers.h"
#include "vecmat.h"
-#include "vector.h"
struct DeviceBase;
struct EffectSlot;
@@ -25,8 +25,6 @@ struct Voice;
struct VoiceChange;
struct VoicePropsItem;
-using uint = unsigned int;
-
constexpr float SpeedOfSoundMetersPerSec{343.3f};
@@ -137,7 +135,7 @@ struct ContextBase {
std::thread mEventThread;
al::semaphore mEventSem;
std::unique_ptr<RingBuffer> mAsyncEvents;
- using AsyncEventBitset = std::bitset<AsyncEvent::UserEventCount>;
+ using AsyncEventBitset = std::bitset<al::to_underlying(AsyncEnableBits::Count)>;
std::atomic<AsyncEventBitset> mEnabledEvts{0u};
/* Asynchronous voice change actions are processed as a linked list of
@@ -146,20 +144,20 @@ struct ContextBase {
* in clusters that are stored in a vector for easy automatic cleanup.
*/
using VoiceChangeCluster = std::unique_ptr<VoiceChange[]>;
- al::vector<VoiceChangeCluster> mVoiceChangeClusters;
+ std::vector<VoiceChangeCluster> mVoiceChangeClusters;
using VoiceCluster = std::unique_ptr<Voice[]>;
- al::vector<VoiceCluster> mVoiceClusters;
+ std::vector<VoiceCluster> mVoiceClusters;
using VoicePropsCluster = std::unique_ptr<VoicePropsItem[]>;
- al::vector<VoicePropsCluster> mVoicePropClusters;
+ std::vector<VoicePropsCluster> mVoicePropClusters;
static constexpr size_t EffectSlotClusterSize{4};
EffectSlot *getEffectSlot();
using EffectSlotCluster = std::unique_ptr<EffectSlot[]>;
- al::vector<EffectSlotCluster> mEffectSlotClusters;
+ std::vector<EffectSlotCluster> mEffectSlotClusters;
ContextBase(DeviceBase *device);
diff --git a/core/converter.cpp b/core/converter.cpp
index a5141448..5b2f3e15 100644
--- a/core/converter.cpp
+++ b/core/converter.cpp
@@ -6,12 +6,12 @@
#include <algorithm>
#include <cassert>
#include <cmath>
+#include <cstddef>
#include <cstdint>
#include <iterator>
#include <limits.h>
#include "albit.h"
-#include "albyte.h"
#include "alnumeric.h"
#include "fpu_ctrl.h"
@@ -219,7 +219,7 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
const uint SrcFrameSize{static_cast<uint>(mChan.size()) * mSrcTypeSize};
const uint DstFrameSize{static_cast<uint>(mChan.size()) * mDstTypeSize};
const uint increment{mIncrement};
- auto SamplesIn = static_cast<const al::byte*>(*src);
+ auto SamplesIn = static_cast<const std::byte*>(*src);
uint NumSrcSamples{*srcframes};
FPUCtl mixer_mode{};
@@ -265,8 +265,8 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
for(size_t chan{0u};chan < mChan.size();chan++)
{
- const al::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan};
- al::byte *DstSamples = static_cast<al::byte*>(dst) + mDstTypeSize*chan;
+ const std::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan};
+ std::byte *DstSamples = static_cast<std::byte*>(dst) + mDstTypeSize*chan;
/* Load the previous samples into the source data first, then the
* new samples from the input buffer.
@@ -299,7 +299,7 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
SamplesIn += SrcFrameSize*srcread;
NumSrcSamples -= srcread;
- dst = static_cast<al::byte*>(dst) + DstFrameSize*DstSize;
+ dst = static_cast<std::byte*>(dst) + DstFrameSize*DstSize;
pos += DstSize;
}
@@ -309,6 +309,98 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
return pos;
}
+uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes)
+{
+ const uint increment{mIncrement};
+ uint NumSrcSamples{*srcframes};
+
+ FPUCtl mixer_mode{};
+ uint pos{0};
+ while(pos < dstframes && NumSrcSamples > 0)
+ {
+ const uint prepcount{mSrcPrepCount};
+ const uint readable{minu(NumSrcSamples, BufferLineSize - prepcount)};
+
+ if(prepcount < MaxResamplerPadding && MaxResamplerPadding-prepcount >= readable)
+ {
+ /* Not enough input samples to generate an output sample. Store
+ * what we're given for later.
+ */
+ for(size_t chan{0u};chan < mChan.size();chan++)
+ {
+ LoadSamples(&mChan[chan].PrevSamples[prepcount],
+ static_cast<const std::byte*>(src[chan]), 1, mSrcType, readable);
+ src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*readable;
+ }
+
+ mSrcPrepCount = prepcount + readable;
+ NumSrcSamples = 0;
+ break;
+ }
+
+ float *RESTRICT SrcData{mSrcSamples};
+ float *RESTRICT DstData{mDstSamples};
+ uint DataPosFrac{mFracOffset};
+ uint64_t DataSize64{prepcount};
+ DataSize64 += readable;
+ DataSize64 -= MaxResamplerPadding;
+ DataSize64 <<= MixerFracBits;
+ DataSize64 -= DataPosFrac;
+
+ /* If we have a full prep, we can generate at least one sample. */
+ auto DstSize = static_cast<uint>(
+ clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize));
+ DstSize = minu(DstSize, dstframes-pos);
+
+ const uint DataPosEnd{DstSize*increment + DataPosFrac};
+ const uint SrcDataEnd{DataPosEnd>>MixerFracBits};
+
+ assert(prepcount+readable >= SrcDataEnd);
+ const uint nextprep{minu(prepcount + readable - SrcDataEnd, MaxResamplerPadding)};
+
+ for(size_t chan{0u};chan < mChan.size();chan++)
+ {
+ /* Load the previous samples into the source data first, then the
+ * new samples from the input buffer.
+ */
+ std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData);
+ LoadSamples(SrcData + prepcount, src[chan], 1, mSrcType, readable);
+
+ /* Store as many prep samples for next time as possible, given the
+ * number of output samples being generated.
+ */
+ std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples);
+ std::fill(std::begin(mChan[chan].PrevSamples)+nextprep,
+ std::end(mChan[chan].PrevSamples), 0.0f);
+
+ /* Now resample, and store the result in the output buffer. */
+ mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment,
+ {DstData, DstSize});
+
+ std::byte *DstSamples = static_cast<std::byte*>(dst[chan]) + pos*mDstTypeSize;
+ StoreSamples(DstSamples, DstData, 1, mDstType, DstSize);
+ }
+
+ /* Update the number of prep samples still available, as well as the
+ * fractional offset.
+ */
+ mSrcPrepCount = nextprep;
+ mFracOffset = DataPosEnd & MixerFracMask;
+
+ /* Update the src and dst pointers in case there's still more to do. */
+ const uint srcread{minu(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)};
+ for(size_t chan{0u};chan < mChan.size();chan++)
+ src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*srcread;
+ NumSrcSamples -= srcread;
+
+ pos += DstSize;
+ }
+
+ *srcframes = NumSrcSamples;
+
+ return pos;
+}
+
void ChannelConverter::convert(const void *src, float *dst, uint frames) const
{
diff --git a/core/converter.h b/core/converter.h
index 01becea2..49ca124d 100644
--- a/core/converter.h
+++ b/core/converter.h
@@ -36,6 +36,7 @@ struct SampleConverter {
SampleConverter(size_t numchans) : mChan{numchans} { }
uint convert(const void **src, uint *srcframes, void *dst, uint dstframes);
+ uint convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes);
uint availableOut(uint srcframes) const;
using SampleOffset = std::chrono::duration<int64_t, std::ratio<1,MixerFracOne>>;
diff --git a/core/cpu_caps.cpp b/core/cpu_caps.cpp
index d4b4d86c..1a064cf4 100644
--- a/core/cpu_caps.cpp
+++ b/core/cpu_caps.cpp
@@ -17,6 +17,7 @@
#include <intrin.h>
#endif
+#include <algorithm>
#include <array>
#include <cctype>
#include <string>
@@ -50,14 +51,14 @@ inline std::array<reg_type,4> get_cpuid(unsigned int f)
} // namespace
-al::optional<CPUInfo> GetCPUInfo()
+std::optional<CPUInfo> GetCPUInfo()
{
CPUInfo ret;
#ifdef CAN_GET_CPUID
auto cpuregs = get_cpuid(0);
if(cpuregs[0] == 0)
- return al::nullopt;
+ return std::nullopt;
const reg_type maxfunc{cpuregs[0]};
diff --git a/core/cpu_caps.h b/core/cpu_caps.h
index ffd671d0..0826a49b 100644
--- a/core/cpu_caps.h
+++ b/core/cpu_caps.h
@@ -1,10 +1,9 @@
#ifndef CORE_CPU_CAPS_H
#define CORE_CPU_CAPS_H
+#include <optional>
#include <string>
-#include "aloptional.h"
-
extern int CPUCapFlags;
enum {
@@ -21,6 +20,6 @@ struct CPUInfo {
int mCaps{0};
};
-al::optional<CPUInfo> GetCPUInfo();
+std::optional<CPUInfo> GetCPUInfo();
#endif /* CORE_CPU_CAPS_H */
diff --git a/core/cubic_tables.cpp b/core/cubic_tables.cpp
index 73ec6b3f..5e7aafad 100644
--- a/core/cubic_tables.cpp
+++ b/core/cubic_tables.cpp
@@ -1,24 +1,16 @@
#include "cubic_tables.h"
-#include <algorithm>
#include <array>
-#include <cassert>
-#include <cmath>
-#include <limits>
-#include <memory>
-#include <stdexcept>
+#include <stddef.h>
-#include "alnumbers.h"
-#include "core/mixer/defs.h"
+#include "cubic_defs.h"
namespace {
-using uint = unsigned int;
-
struct SplineFilterArray {
- alignas(16) CubicCoefficients mTable[CubicPhaseCount]{};
+ alignas(16) std::array<CubicCoefficients,CubicPhaseCount> mTable{};
constexpr SplineFilterArray()
{
@@ -49,7 +41,7 @@ struct SplineFilterArray {
mTable[pi].mDeltas[3] = -mTable[pi].mCoeffs[3];
}
- constexpr auto getTable() const noexcept { return al::as_span(mTable); }
+ constexpr auto& getTable() const noexcept { return mTable; }
};
constexpr SplineFilterArray SplineFilter{};
diff --git a/core/dbus_wrap.cpp b/core/dbus_wrap.cpp
index 7f221706..48419566 100644
--- a/core/dbus_wrap.cpp
+++ b/core/dbus_wrap.cpp
@@ -8,20 +8,16 @@
#include <mutex>
#include <type_traits>
+#include "albit.h"
#include "logging.h"
-void *dbus_handle{nullptr};
-#define DECL_FUNC(x) decltype(p##x) p##x{};
-DBUS_FUNCTIONS(DECL_FUNC)
-#undef DECL_FUNC
-
void PrepareDBus()
{
static constexpr char libname[] = "libdbus-1.so.3";
auto load_func = [](auto &f, const char *name) -> void
- { f = reinterpret_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); };
+ { f = al::bit_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); };
#define LOAD_FUNC(x) do { \
load_func(p##x, #x); \
if(!p##x) \
diff --git a/core/dbus_wrap.h b/core/dbus_wrap.h
index 09eaacf9..65f08942 100644
--- a/core/dbus_wrap.h
+++ b/core/dbus_wrap.h
@@ -28,8 +28,8 @@ MAGIC(dbus_message_iter_get_arg_type) \
MAGIC(dbus_message_iter_get_basic) \
MAGIC(dbus_set_error_from_message)
-extern void *dbus_handle;
-#define DECL_FUNC(x) extern decltype(x) *p##x;
+inline void *dbus_handle{};
+#define DECL_FUNC(x) inline decltype(x) *p##x{};
DBUS_FUNCTIONS(DECL_FUNC)
#undef DECL_FUNC
diff --git a/core/device.h b/core/device.h
index 9aaf7adb..b1ffc9ce 100644
--- a/core/device.h
+++ b/core/device.h
@@ -1,14 +1,13 @@
#ifndef CORE_DEVICE_H
#define CORE_DEVICE_H
-#include <stddef.h>
-
#include <array>
#include <atomic>
#include <bitset>
#include <chrono>
#include <memory>
-#include <mutex>
+#include <stddef.h>
+#include <stdint.h>
#include <string>
#include "almalloc.h"
@@ -43,20 +42,20 @@ using uint = unsigned int;
#define DEFAULT_NUM_UPDATES 3
-enum class DeviceType : unsigned char {
+enum class DeviceType : uint8_t {
Playback,
Capture,
Loopback
};
-enum class RenderMode : unsigned char {
+enum class RenderMode : uint8_t {
Normal,
Pairwise,
Hrtf
};
-enum class StereoEncoding : unsigned char {
+enum class StereoEncoding : uint8_t {
Basic,
Uhj,
Hrtf,
@@ -95,7 +94,7 @@ struct DistanceComp {
};
-constexpr uint InvalidChannelIndex{~0u};
+constexpr uint8_t InvalidChannelIndex{static_cast<uint8_t>(~0u)};
struct BFChannelConfig {
float Scale;
@@ -113,8 +112,8 @@ struct MixParams {
* source is expected to be a 3D ACN/N3D ambisonic buffer, and for each
* channel [0...count), the given functor is called with the source channel
* index, destination channel index, and the gain for that channel. If the
- * destination channel is INVALID_CHANNEL_INDEX, the given source channel
- * is not used for output.
+ * destination channel is InvalidChannelIndex, the given source channel is
+ * not used for output.
*/
template<typename F>
void setAmbiMixParams(const MixParams &inmix, const float gainbase, F func) const
@@ -123,14 +122,14 @@ struct MixParams {
const size_t numOut{Buffer.size()};
for(size_t i{0};i < numIn;++i)
{
- auto idx = InvalidChannelIndex;
- auto gain = 0.0f;
+ uint8_t idx{InvalidChannelIndex};
+ float gain{0.0f};
for(size_t j{0};j < numOut;++j)
{
if(AmbiMap[j].Index == inmix.AmbiMap[i].Index)
{
- idx = static_cast<uint>(j);
+ idx = static_cast<uint8_t>(j);
gain = AmbiMap[j].Scale * gainbase;
break;
}
@@ -142,7 +141,7 @@ struct MixParams {
struct RealMixParams {
al::span<const InputRemixMap> RemixMap;
- std::array<uint,MaxChannels> ChannelIndex{};
+ std::array<uint8_t,MaxChannels> ChannelIndex{};
al::span<FloatBufferLine> Buffer;
};
@@ -166,6 +165,11 @@ enum {
// ear buds, etc).
DirectEar,
+ /* Specifies if output is using speaker virtualization (e.g. Windows
+ * Spatial Audio).
+ */
+ Virtualization,
+
DeviceFlagsCount
};
@@ -325,9 +329,9 @@ struct DeviceBase {
/**
* Returns the index for the given channel name (e.g. FrontCenter), or
- * INVALID_CHANNEL_INDEX if it doesn't exist.
+ * InvalidChannelIndex if it doesn't exist.
*/
- uint channelIdxByName(Channel chan) const noexcept
+ uint8_t channelIdxByName(Channel chan) const noexcept
{ return RealOut.ChannelIndex[chan]; }
DISABLE_ALLOC()
diff --git a/core/effects/base.h b/core/effects/base.h
index 4ee19f37..83df7cf0 100644
--- a/core/effects/base.h
+++ b/core/effects/base.h
@@ -1,9 +1,9 @@
#ifndef CORE_EFFECTS_BASE_H
#define CORE_EFFECTS_BASE_H
+#include <array>
#include <stddef.h>
-#include "albyte.h"
#include "almalloc.h"
#include "alspan.h"
#include "atomic.h"
@@ -166,6 +166,11 @@ union EffectProps {
struct {
float Gain;
} Dedicated;
+
+ struct {
+ std::array<float,3> OrientAt;
+ std::array<float,3> OrientUp;
+ } Convolution;
};
diff --git a/core/filters/nfc.h b/core/filters/nfc.h
index 33f67a5f..4b8e68b5 100644
--- a/core/filters/nfc.h
+++ b/core/filters/nfc.h
@@ -39,7 +39,7 @@ public:
* w1 = speed_of_sound / (control_distance * sample_rate);
*
* Generally speaking, the control distance should be approximately the
- * average speaker distance, or based on the reference delay if outputing
+ * average speaker distance, or based on the reference delay if outputting
* NFC-HOA. It must not be negative, 0, or infinite. The source distance
* should not be too small relative to the control distance.
*/
diff --git a/core/fmt_traits.h b/core/fmt_traits.h
index f797f836..02473014 100644
--- a/core/fmt_traits.h
+++ b/core/fmt_traits.h
@@ -1,10 +1,9 @@
#ifndef CORE_FMT_TRAITS_H
#define CORE_FMT_TRAITS_H
-#include <stddef.h>
+#include <cstddef>
#include <stdint.h>
-#include "albyte.h"
#include "buffer_storage.h"
@@ -22,36 +21,43 @@ struct FmtTypeTraits<FmtUByte> {
using Type = uint8_t;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept
- { return val*OutT{1.0/128.0} - OutT{1.0}; }
+ static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/128.0} - OutT{1.0}; }
};
template<>
struct FmtTypeTraits<FmtShort> {
using Type = int16_t;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; }
+ static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; }
+};
+template<>
+struct FmtTypeTraits<FmtInt> {
+ using Type = int32_t;
+
+ template<typename OutT>
+ static constexpr OutT to(const Type val) noexcept
+ { return static_cast<OutT>(val)*OutT{1.0/2147483648.0}; }
};
template<>
struct FmtTypeTraits<FmtFloat> {
using Type = float;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept { return val; }
+ static constexpr OutT to(const Type val) noexcept { return val; }
};
template<>
struct FmtTypeTraits<FmtDouble> {
using Type = double;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept { return static_cast<OutT>(val); }
+ static constexpr OutT to(const Type val) noexcept { return static_cast<OutT>(val); }
};
template<>
struct FmtTypeTraits<FmtMulaw> {
using Type = uint8_t;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept
+ static constexpr OutT to(const Type val) noexcept
{ return muLawDecompressionTable[val] * OutT{1.0/32768.0}; }
};
template<>
@@ -59,14 +65,14 @@ struct FmtTypeTraits<FmtAlaw> {
using Type = uint8_t;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept
+ static constexpr OutT to(const Type val) noexcept
{ return aLawDecompressionTable[val] * OutT{1.0/32768.0}; }
};
template<FmtType SrcType, typename DstT>
-inline void LoadSampleArray(DstT *RESTRICT dst, const al::byte *src, const size_t srcstep,
- const size_t samples) noexcept
+inline void LoadSampleArray(DstT *RESTRICT dst, const std::byte *src, const std::size_t srcstep,
+ const std::size_t samples) noexcept
{
using TypeTraits = FmtTypeTraits<SrcType>;
using SampleType = typename TypeTraits::Type;
diff --git a/core/fpu_ctrl.cpp b/core/fpu_ctrl.cpp
index 0cf0d6e7..435855ad 100644
--- a/core/fpu_ctrl.cpp
+++ b/core/fpu_ctrl.cpp
@@ -8,38 +8,71 @@
#endif
#ifdef HAVE_SSE_INTRINSICS
#include <emmintrin.h>
-#ifndef _MM_DENORMALS_ZERO_MASK
+#elif defined(HAVE_SSE)
+#include <xmmintrin.h>
+#endif
+
+#if defined(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
-#endif
#include "cpu_caps.h"
+namespace {
-void FPUCtl::enter() noexcept
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+[[gnu::target("sse")]]
+#endif
+[[maybe_unused]]
+void disable_denormals(unsigned int *state [[maybe_unused]])
{
- if(this->in_mode) return;
-
#if defined(HAVE_SSE_INTRINSICS)
- this->sse_state = _mm_getcsr();
- unsigned int sseState{this->sse_state};
+ *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(__GNUC__) && defined(HAVE_SSE)
+#elif defined(HAVE_SSE)
- if((CPUCapFlags&CPU_CAP_SSE))
+ *state = _mm_getcsr();
+ unsigned int sseState{*state};
+ sseState &= ~_MM_FLUSH_ZERO_MASK;
+ sseState |= _MM_FLUSH_ZERO_ON;
+ if((CPUCapFlags&CPU_CAP_SSE2))
{
- __asm__ __volatile__("stmxcsr %0" : "=m" (*&this->sse_state));
- unsigned int sseState{this->sse_state};
- sseState |= 0x8000; /* set flush-to-zero */
- if((CPUCapFlags&CPU_CAP_SSE2))
- sseState |= 0x0040; /* set denormals-are-zero */
- __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
+ sseState &= ~_MM_DENORMALS_ZERO_MASK;
+ sseState |= _MM_DENORMALS_ZERO_ON;
}
+ _mm_setcsr(sseState);
+#endif
+}
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+[[gnu::target("sse")]]
+#endif
+[[maybe_unused]]
+void reset_fpu(unsigned int state [[maybe_unused]])
+{
+#if defined(HAVE_SSE_INTRINSICS) || defined(HAVE_SSE)
+ _mm_setcsr(state);
+#endif
+}
+
+} // namespace
+
+
+void FPUCtl::enter() noexcept
+{
+ if(this->in_mode) return;
+
+#if defined(HAVE_SSE_INTRINSICS)
+ disable_denormals(&this->sse_state);
+#elif defined(HAVE_SSE)
+ if((CPUCapFlags&CPU_CAP_SSE))
+ disable_denormals(&this->sse_state);
#endif
this->in_mode = true;
@@ -50,12 +83,10 @@ void FPUCtl::leave() noexcept
if(!this->in_mode) return;
#if defined(HAVE_SSE_INTRINSICS)
- _mm_setcsr(this->sse_state);
-
-#elif defined(__GNUC__) && defined(HAVE_SSE)
-
+ reset_fpu(this->sse_state);
+#elif defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
- __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state));
+ reset_fpu(this->sse_state);
#endif
this->in_mode = false;
}
diff --git a/core/helpers.cpp b/core/helpers.cpp
index 99cf009c..5a996eee 100644
--- a/core/helpers.cpp
+++ b/core/helpers.cpp
@@ -3,29 +3,27 @@
#include "helpers.h"
+#if defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
#include <algorithm>
-#include <cerrno>
-#include <cstdarg>
#include <cstdlib>
-#include <cstdio>
#include <cstring>
-#include <mutex>
#include <limits>
+#include <mutex>
+#include <optional>
#include <string>
-#include <tuple>
-#include "almalloc.h"
-#include "alfstream.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "alstring.h"
#include "logging.h"
#include "strutils.h"
-#include "vector.h"
-/* Mixing thread piority level */
+/* Mixing thread priority level */
int RTPrioLevel{1};
/* Allow reducing the process's RTTime limit for RTKit. */
@@ -34,14 +32,15 @@ bool AllowRTTimeLimit{true};
#ifdef _WIN32
+#include <cctype>
#include <shlobj.h>
const PathNamePair &GetProcBinary()
{
- static al::optional<PathNamePair> procbin;
+ static std::optional<PathNamePair> procbin;
if(procbin) return *procbin;
-
- auto fullpath = al::vector<WCHAR>(256);
+#if !defined(ALSOFT_UWP)
+ auto fullpath = std::vector<WCHAR>(256);
DWORD len{GetModuleFileNameW(nullptr, fullpath.data(), static_cast<DWORD>(fullpath.size()))};
while(len == fullpath.size())
{
@@ -58,7 +57,16 @@ const PathNamePair &GetProcBinary()
fullpath.resize(len);
if(fullpath.back() != 0)
fullpath.push_back(0);
-
+#else
+ auto exePath = __wargv[0];
+ if (!exePath)
+ {
+ ERR("Failed to get process name: error %lu\n", GetLastError());
+ procbin.emplace();
+ return *procbin;
+ }
+ std::vector<WCHAR> fullpath{exePath, exePath + wcslen(exePath) + 1};
+#endif
std::replace(fullpath.begin(), fullpath.end(), '/', '\\');
auto sep = std::find(fullpath.rbegin()+1, fullpath.rend(), '\\');
if(sep != fullpath.rend())
@@ -75,16 +83,16 @@ const PathNamePair &GetProcBinary()
namespace {
-void DirectorySearch(const char *path, const char *ext, al::vector<std::string> *const results)
+void DirectorySearch(const char *path, const char *ext, std::vector<std::string> *const results)
{
std::string pathstr{path};
pathstr += "\\*";
pathstr += ext;
TRACE("Searching %s\n", pathstr.c_str());
- std::wstring wpath{utf8_to_wstr(pathstr.c_str())};
+ std::wstring wpath{utf8_to_wstr(pathstr)};
WIN32_FIND_DATAW fdata;
- HANDLE hdl{FindFirstFileW(wpath.c_str(), &fdata)};
+ HANDLE hdl{FindFirstFileExW(wpath.c_str(), FindExInfoStandard, &fdata, FindExSearchNameMatch, NULL, 0)};
if(hdl == INVALID_HANDLE_VALUE) return;
const auto base = results->size();
@@ -97,7 +105,6 @@ void DirectorySearch(const char *path, const char *ext, al::vector<std::string>
str += wstr_to_utf8(fdata.cFileName);
} while(FindNextFileW(hdl, &fdata));
FindClose(hdl);
-
const al::span<std::string> newlist{results->data()+base, results->size()-base};
std::sort(newlist.begin(), newlist.end());
for(const auto &name : newlist)
@@ -106,16 +113,16 @@ void DirectorySearch(const char *path, const char *ext, al::vector<std::string>
} // namespace
-al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
+std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
{
- auto is_slash = [](int c) noexcept -> int { return (c == '\\' || c == '/'); };
+ auto is_slash = [](int c) noexcept { return (c == '\\' || c == '/'); };
static std::mutex search_lock;
std::lock_guard<std::mutex> _{search_lock};
/* If the path is absolute, use it directly. */
- al::vector<std::string> results;
- if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2]))
+ std::vector<std::string> results;
+ if(std::isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2]))
{
std::string path{subdir};
std::replace(path.begin(), path.end(), '/', '\\');
@@ -149,9 +156,9 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
std::replace(path.begin(), path.end(), '/', '\\');
DirectorySearch(path.c_str(), ext, &results);
+#if !defined(ALSOFT_UWP) && !defined(_GAMING_XBOX)
/* Search the local and global data dirs. */
- static const int ids[2]{ CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
- for(int id : ids)
+ for(auto id : std::array{CSIDL_APPDATA, CSIDL_COMMON_APPDATA})
{
WCHAR buffer[MAX_PATH];
if(SHGetSpecialFolderPathW(nullptr, buffer, id, FALSE) == FALSE)
@@ -165,24 +172,27 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
DirectorySearch(path.c_str(), ext, &results);
}
+#endif
return results;
}
void SetRTPriority(void)
{
+#if !defined(ALSOFT_UWP)
if(RTPrioLevel > 0)
{
if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
ERR("Failed to set priority level for thread\n");
}
+#endif
}
#else
-#include <sys/types.h>
-#include <unistd.h>
+#include <cerrno>
#include <dirent.h>
+#include <unistd.h>
#ifdef __FreeBSD__
#include <sys/sysctl.h>
#endif
@@ -197,7 +207,6 @@ void SetRTPriority(void)
#include <sched.h>
#endif
#ifdef HAVE_RTKIT
-#include <sys/time.h>
#include <sys/resource.h>
#include "dbus_wrap.h"
@@ -209,10 +218,10 @@ void SetRTPriority(void)
const PathNamePair &GetProcBinary()
{
- static al::optional<PathNamePair> procbin;
+ static std::optional<PathNamePair> procbin;
if(procbin) return *procbin;
- al::vector<char> pathname;
+ std::vector<char> pathname;
#ifdef __FreeBSD__
size_t pathlen;
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
@@ -247,7 +256,7 @@ const PathNamePair &GetProcBinary()
#ifndef __SWITCH__
if(pathname.empty())
{
- static const char SelfLinkNames[][32]{
+ const char *SelfLinkNames[]{
"/proc/self/exe",
"/proc/self/file",
"/proc/curproc/exe",
@@ -295,7 +304,7 @@ const PathNamePair &GetProcBinary()
namespace {
-void DirectorySearch(const char *path, const char *ext, al::vector<std::string> *const results)
+void DirectorySearch(const char *path, const char *ext, std::vector<std::string> *const results)
{
TRACE("Searching %s for *%s\n", path, ext);
DIR *dir{opendir(path)};
@@ -331,12 +340,12 @@ void DirectorySearch(const char *path, const char *ext, al::vector<std::string>
} // namespace
-al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
+std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
{
static std::mutex search_lock;
std::lock_guard<std::mutex> _{search_lock};
- al::vector<std::string> results;
+ std::vector<std::string> results;
if(subdir[0] == '/')
{
DirectorySearch(subdir, ext, &results);
@@ -348,7 +357,7 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
DirectorySearch(localpath->c_str(), ext, &results);
else
{
- al::vector<char> cwdbuf(256);
+ std::vector<char> cwdbuf(256);
while(!getcwd(cwdbuf.data(), cwdbuf.size()))
{
if(errno != ERANGE)
@@ -425,7 +434,7 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
namespace {
-bool SetRTPriorityPthread(int prio)
+bool SetRTPriorityPthread(int prio [[maybe_unused]])
{
int err{ENOTSUP};
#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
@@ -445,16 +454,12 @@ bool SetRTPriorityPthread(int prio)
#endif
err = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
if(err == 0) return true;
-
-#else
-
- std::ignore = prio;
#endif
WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err), err);
return false;
}
-bool SetRTPriorityRTKit(int prio)
+bool SetRTPriorityRTKit(int prio [[maybe_unused]])
{
#ifdef HAVE_RTKIT
if(!HasDBus())
@@ -547,7 +552,6 @@ bool SetRTPriorityRTKit(int prio)
#else
- std::ignore = prio;
WARN("D-Bus not supported\n");
#endif
return false;
diff --git a/core/helpers.h b/core/helpers.h
index f0bfcf1b..df51c116 100644
--- a/core/helpers.h
+++ b/core/helpers.h
@@ -1,18 +1,26 @@
#ifndef CORE_HELPERS_H
#define CORE_HELPERS_H
+#include <utility>
#include <string>
+#include <vector>
-#include "vector.h"
+struct PathNamePair {
+ std::string path, fname;
-struct PathNamePair { std::string path, fname; };
+ PathNamePair() = default;
+ template<typename T, typename U>
+ PathNamePair(T&& path_, U&& fname_)
+ : path{std::forward<T>(path_)}, fname{std::forward<U>(fname_)}
+ { }
+};
const PathNamePair &GetProcBinary(void);
extern int RTPrioLevel;
extern bool AllowRTTimeLimit;
void SetRTPriority(void);
-al::vector<std::string> SearchDataFiles(const char *match, const char *subdir);
+std::vector<std::string> SearchDataFiles(const char *match, const char *subdir);
#endif /* CORE_HELPERS_H */
diff --git a/core/hrtf.cpp b/core/hrtf.cpp
index d5c7573a..9a13a004 100644
--- a/core/hrtf.cpp
+++ b/core/hrtf.cpp
@@ -8,6 +8,7 @@
#include <cassert>
#include <cctype>
#include <cmath>
+#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
@@ -16,16 +17,16 @@
#include <memory>
#include <mutex>
#include <numeric>
+#include <optional>
#include <type_traits>
#include <utility>
+#include <vector>
#include "albit.h"
-#include "albyte.h"
#include "alfstream.h"
#include "almalloc.h"
#include "alnumbers.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "ambidefs.h"
#include "filters/splitter.h"
@@ -34,7 +35,6 @@
#include "mixer/hrtfdefs.h"
#include "opthelpers.h"
#include "polyphase_resampler.h"
-#include "vector.h"
namespace {
@@ -98,10 +98,10 @@ constexpr char magicMarker03[8]{'M','i','n','P','H','R','0','3'};
constexpr auto PassthruCoeff = static_cast<float>(1.0/al::numbers::sqrt2);
std::mutex LoadedHrtfLock;
-al::vector<LoadedHrtf> LoadedHrtfs;
+std::vector<LoadedHrtf> LoadedHrtfs;
std::mutex EnumeratedHrtfLock;
-al::vector<HrtfEntry> EnumeratedHrtfs;
+std::vector<HrtfEntry> EnumeratedHrtfs;
class databuf final : public std::streambuf {
@@ -289,13 +289,13 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool
mChannels[0].mSplitter.init(static_cast<float>(xover_norm));
for(size_t i{0};i < mChannels.size();++i)
{
- const size_t order{AmbiIndex::OrderFromChannel()[i]};
+ const size_t order{AmbiIndex::OrderFromChannel[i]};
mChannels[i].mSplitter = mChannels[0].mSplitter;
mChannels[i].mHfScale = AmbiOrderHFGain[order];
}
uint min_delay{HrtfHistoryLength*HrirDelayFracOne}, max_delay{0};
- al::vector<ImpulseResponse> impres; impres.reserve(AmbiPoints.size());
+ std::vector<ImpulseResponse> impres; impres.reserve(AmbiPoints.size());
auto calc_res = [Hrtf,&max_delay,&min_delay](const AngularPoint &pt) -> ImpulseResponse
{
auto &field = Hrtf->mFields[0];
@@ -331,7 +331,7 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool
TRACE("Min delay: %.2f, max delay: %.2f, FIR length: %u\n",
min_delay/double{HrirDelayFracOne}, max_delay/double{HrirDelayFracOne}, irSize);
- auto tmpres = al::vector<std::array<double2,HrirLength>>(mChannels.size());
+ auto tmpres = std::vector<std::array<double2,HrirLength>>(mChannels.size());
max_delay = 0;
for(size_t c{0u};c < AmbiPoints.size();++c)
{
@@ -393,7 +393,7 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize,
{
Hrtf.reset(al::construct_at(static_cast<HrtfStore*>(ptr)));
InitRef(Hrtf->mRef, 1u);
- Hrtf->mSampleRate = rate;
+ Hrtf->mSampleRate = rate & 0xff'ff'ff;
Hrtf->mIrSize = irSize;
/* Set up pointers to storage following the main HRTF struct. */
@@ -425,7 +425,7 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize,
std::uninitialized_copy_n(delays, irCount, delays_);
/* Finally, assign the storage pointers. */
- Hrtf->mFields = al::as_span(field_, fields.size());
+ Hrtf->mFields = {field_, fields.size()};
Hrtf->mElev = elev_;
Hrtf->mCoeffs = coeffs_;
Hrtf->mDelays = delays_;
@@ -492,10 +492,10 @@ T> readle(std::istream &data)
static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type");
T ret{};
- al::byte b[sizeof(T)]{};
+ std::byte b[sizeof(T)]{};
if(!data.read(reinterpret_cast<char*>(b), num_bits/8))
return static_cast<T>(EOF);
- std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast<al::byte*>(&ret));
+ std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast<std::byte*>(&ret));
return fixsign<num_bits>(ret);
}
@@ -529,7 +529,7 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename)
return nullptr;
}
- auto elevs = al::vector<HrtfStore::Elevation>(evCount);
+ auto elevs = std::vector<HrtfStore::Elevation>(evCount);
for(auto &elev : elevs)
elev.irOffset = readle<uint16_t>(data);
if(!data || data.eof())
@@ -571,8 +571,8 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename)
return nullptr;
}
- auto coeffs = al::vector<HrirArray>(irCount, HrirArray{});
- auto delays = al::vector<ubyte2>(irCount);
+ auto coeffs = std::vector<HrirArray>(irCount, HrirArray{});
+ auto delays = std::vector<ubyte2>(irCount);
for(auto &hrir : coeffs)
{
for(auto &val : al::span<float2>{hrir.data(), irSize})
@@ -626,7 +626,7 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename)
return nullptr;
}
- auto elevs = al::vector<HrtfStore::Elevation>(evCount);
+ auto elevs = std::vector<HrtfStore::Elevation>(evCount);
for(auto &elev : elevs)
elev.azCount = readle<uint8_t>(data);
if(!data || data.eof())
@@ -649,8 +649,8 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename)
elevs[i].irOffset = static_cast<ushort>(elevs[i-1].irOffset + elevs[i-1].azCount);
const ushort irCount{static_cast<ushort>(elevs.back().irOffset + elevs.back().azCount)};
- auto coeffs = al::vector<HrirArray>(irCount, HrirArray{});
- auto delays = al::vector<ubyte2>(irCount);
+ auto coeffs = std::vector<HrirArray>(irCount, HrirArray{});
+ auto delays = std::vector<ubyte2>(irCount);
for(auto &hrir : coeffs)
{
for(auto &val : al::span<float2>{hrir.data(), irSize})
@@ -722,8 +722,8 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
return nullptr;
}
- auto fields = al::vector<HrtfStore::Field>(fdCount);
- auto elevs = al::vector<HrtfStore::Elevation>{};
+ auto fields = std::vector<HrtfStore::Field>(fdCount);
+ auto elevs = std::vector<HrtfStore::Elevation>{};
for(size_t f{0};f < fdCount;f++)
{
const ushort distance{readle<uint16_t>(data)};
@@ -787,8 +787,8 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
});
const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset);
- auto coeffs = al::vector<HrirArray>(irTotal, HrirArray{});
- auto delays = al::vector<ubyte2>(irTotal);
+ auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{});
+ auto delays = std::vector<ubyte2>(irTotal);
if(channelType == ChanType_LeftOnly)
{
if(sampleType == SampleType_S16)
@@ -881,10 +881,10 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
if(fdCount > 1)
{
- auto fields_ = al::vector<HrtfStore::Field>(fields.size());
- auto elevs_ = al::vector<HrtfStore::Elevation>(elevs.size());
- auto coeffs_ = al::vector<HrirArray>(coeffs.size());
- auto delays_ = al::vector<ubyte2>(delays.size());
+ auto fields_ = std::vector<HrtfStore::Field>(fields.size());
+ auto elevs_ = std::vector<HrtfStore::Elevation>(elevs.size());
+ auto coeffs_ = std::vector<HrirArray>(coeffs.size());
+ auto delays_ = std::vector<ubyte2>(delays.size());
/* Simple reverse for the per-field elements. */
std::reverse_copy(fields.cbegin(), fields.cend(), fields_.begin());
@@ -983,8 +983,8 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename)
return nullptr;
}
- auto fields = al::vector<HrtfStore::Field>(fdCount);
- auto elevs = al::vector<HrtfStore::Elevation>{};
+ auto fields = std::vector<HrtfStore::Field>(fdCount);
+ auto elevs = std::vector<HrtfStore::Elevation>{};
for(size_t f{0};f < fdCount;f++)
{
const ushort distance{readle<uint16_t>(data)};
@@ -1048,8 +1048,8 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename)
});
const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset);
- auto coeffs = al::vector<HrirArray>(irTotal, HrirArray{});
- auto delays = al::vector<ubyte2>(irTotal);
+ auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{});
+ auto delays = std::vector<ubyte2>(irTotal);
if(channelType == ChanType_LeftOnly)
{
for(auto &hrir : coeffs)
@@ -1221,7 +1221,7 @@ al::span<const char> GetResource(int name)
} // namespace
-al::vector<std::string> EnumerateHrtf(al::optional<std::string> pathopt)
+std::vector<std::string> EnumerateHrtf(std::optional<std::string> pathopt)
{
std::lock_guard<std::mutex> _{EnumeratedHrtfLock};
EnumeratedHrtfs.clear();
@@ -1270,7 +1270,7 @@ al::vector<std::string> EnumerateHrtf(al::optional<std::string> pathopt)
AddBuiltInEntry("Built-In HRTF", IDR_DEFAULT_HRTF_MHR);
}
- al::vector<std::string> list;
+ std::vector<std::string> list;
list.reserve(EnumeratedHrtfs.size());
for(auto &entry : EnumeratedHrtfs)
list.emplace_back(entry.mDispName);
@@ -1368,7 +1368,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
TRACE("Resampling HRTF %s (%uhz -> %uhz)\n", name.c_str(), 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(), size_t{0},
+ const size_t lastEv{std::accumulate(hrtf->mFields.begin(), hrtf->mFields.end(), 0_uz,
[](const size_t curval, const HrtfStore::Field &field) noexcept -> size_t
{ return curval + field.evCount; }
) - 1};
@@ -1394,7 +1394,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
/* Scale the delays for the new sample rate. */
float max_delay{0.0f};
- auto new_delays = al::vector<float2>(irCount);
+ auto new_delays = std::vector<float2>(irCount);
const float rate_scale{static_cast<float>(devrate)/static_cast<float>(hrtf->mSampleRate)};
for(size_t i{0};i < irCount;++i)
{
@@ -1430,7 +1430,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
*/
const float newIrSize{std::round(static_cast<float>(hrtf->mIrSize) * rate_scale)};
hrtf->mIrSize = static_cast<uint8_t>(minf(HrirLength, newIrSize));
- hrtf->mSampleRate = devrate;
+ hrtf->mSampleRate = devrate & 0xff'ff'ff;
}
TRACE("Loaded HRTF %s for sample rate %uhz, %u-sample filter\n", name.c_str(),
diff --git a/core/hrtf.h b/core/hrtf.h
index eb18682a..5e6e09a8 100644
--- a/core/hrtf.h
+++ b/core/hrtf.h
@@ -4,17 +4,17 @@
#include <array>
#include <cstddef>
#include <memory>
+#include <optional>
#include <string>
+#include <vector>
#include "almalloc.h"
-#include "aloptional.h"
#include "alspan.h"
#include "atomic.h"
#include "ambidefs.h"
#include "bufferline.h"
#include "mixer/hrtfdefs.h"
#include "intrusive_ptr.h"
-#include "vector.h"
struct HrtfStore {
@@ -83,7 +83,7 @@ struct DirectHrtfState {
};
-al::vector<std::string> EnumerateHrtf(al::optional<std::string> pathopt);
+std::vector<std::string> EnumerateHrtf(std::optional<std::string> pathopt);
HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate);
#endif /* CORE_HRTF_H */
diff --git a/core/logging.cpp b/core/logging.cpp
index 34a95e5a..56ad0a0d 100644
--- a/core/logging.cpp
+++ b/core/logging.cpp
@@ -3,13 +3,17 @@
#include "logging.h"
+#include <cctype>
#include <cstdarg>
#include <cstdio>
+#include <cstring>
+#include <mutex>
+#include <optional>
#include <string>
+#include <vector>
#include "alspan.h"
#include "strutils.h"
-#include "vector.h"
#if defined(_WIN32)
@@ -19,22 +23,74 @@
#include <android/log.h>
#endif
-void al_print(LogLevel level, FILE *logfile, const char *fmt, ...)
+
+FILE *gLogFile{stderr};
+#ifdef _DEBUG
+LogLevel gLogLevel{LogLevel::Warning};
+#else
+LogLevel gLogLevel{LogLevel::Error};
+#endif
+
+
+namespace {
+
+enum class LogState : uint8_t {
+ FirstRun,
+ Ready,
+ Disable
+};
+
+std::mutex LogCallbackMutex;
+LogState gLogState{LogState::FirstRun};
+
+LogCallbackFunc gLogCallback{};
+void *gLogCallbackPtr{};
+
+constexpr std::optional<char> GetLevelCode(LogLevel level)
+{
+ switch(level)
+ {
+ case LogLevel::Disable: break;
+ case LogLevel::Error: return 'E';
+ case LogLevel::Warning: return 'W';
+ case LogLevel::Trace: return 'I';
+ }
+ return std::nullopt;
+}
+
+} // namespace
+
+void al_set_log_callback(LogCallbackFunc callback, void *userptr)
+{
+ auto cblock = std::lock_guard{LogCallbackMutex};
+ gLogCallback = callback;
+ gLogCallbackPtr = callback ? userptr : nullptr;
+ if(gLogState == LogState::FirstRun)
+ {
+ auto extlogopt = al::getenv("ALSOFT_DISABLE_LOG_CALLBACK");
+ if(!extlogopt || *extlogopt != "1")
+ gLogState = LogState::Ready;
+ else
+ gLogState = LogState::Disable;
+ }
+}
+
+void al_print(LogLevel level, const char *fmt, ...)
{
/* 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::as_span("[ALSOFT] (--) ").first<14>();
+ auto prefix = al::span{"[ALSOFT] (--) "}.first<14>();
switch(level)
{
case LogLevel::Disable: break;
- case LogLevel::Error: prefix = al::as_span("[ALSOFT] (EE) ").first<14>(); break;
- case LogLevel::Warning: prefix = al::as_span("[ALSOFT] (WW) ").first<14>(); break;
- case LogLevel::Trace: prefix = al::as_span("[ALSOFT] (II) ").first<14>(); 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;
}
- al::vector<char> dynmsg;
+ std::vector<char> dynmsg;
std::array<char,256> stcmsg{};
char *str{stcmsg.data()};
@@ -45,21 +101,28 @@ void al_print(LogLevel level, FILE *logfile, const char *fmt, ...)
va_start(args, fmt);
va_copy(args2, args);
const int msglen{std::vsnprintf(msg.data(), msg.size(), fmt, args)};
- if(msglen >= 0 && static_cast<size_t>(msglen) >= msg.size()) UNLIKELY
+ if(msglen >= 0)
{
- dynmsg.resize(static_cast<size_t>(msglen)+prefix.size() + 1u);
+ 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()};
+ 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);
+ 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);
if(gLogLevel >= level)
{
+ auto logfile = gLogFile;
fputs(str, logfile);
fflush(logfile);
}
@@ -86,4 +149,21 @@ void al_print(LogLevel level, FILE *logfile, const char *fmt, ...)
};
__android_log_print(android_severity(level), "openal", "%s", 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(gLogCallback)
+ gLogCallback(gLogCallbackPtr, *logcode, msg.data(), static_cast<int>(msg.size()));
+ else if(gLogState == LogState::FirstRun)
+ gLogState = LogState::Disable;
+ }
+ }
}
diff --git a/core/logging.h b/core/logging.h
index f4b6ab56..06b7cdde 100644
--- a/core/logging.h
+++ b/core/logging.h
@@ -16,36 +16,23 @@ extern LogLevel gLogLevel;
extern FILE *gLogFile;
-#ifdef __USE_MINGW_ANSI_STDIO
-[[gnu::format(gnu_printf,3,4)]]
-#else
-[[gnu::format(printf,3,4)]]
-#endif
-void al_print(LogLevel level, FILE *logfile, const char *fmt, ...);
-#if (!defined(_WIN32) || defined(NDEBUG)) && !defined(__ANDROID__)
-#define TRACE(...) do { \
- if(gLogLevel >= LogLevel::Trace) UNLIKELY \
- al_print(LogLevel::Trace, gLogFile, __VA_ARGS__); \
-} while(0)
+using LogCallbackFunc = void(*)(void *userptr, char level, const char *message, int length) noexcept;
-#define WARN(...) do { \
- if(gLogLevel >= LogLevel::Warning) UNLIKELY \
- al_print(LogLevel::Warning, gLogFile, __VA_ARGS__); \
-} while(0)
+void al_set_log_callback(LogCallbackFunc callback, void *userptr);
-#define ERR(...) do { \
- if(gLogLevel >= LogLevel::Error) UNLIKELY \
- al_print(LogLevel::Error, gLogFile, __VA_ARGS__); \
-} while(0)
+#ifdef __USE_MINGW_ANSI_STDIO
+[[gnu::format(gnu_printf,2,3)]]
#else
+[[gnu::format(printf,2,3)]]
+#endif
+void al_print(LogLevel level, const char *fmt, ...);
-#define TRACE(...) al_print(LogLevel::Trace, gLogFile, __VA_ARGS__)
+#define TRACE(...) al_print(LogLevel::Trace, __VA_ARGS__)
-#define WARN(...) al_print(LogLevel::Warning, gLogFile, __VA_ARGS__)
+#define WARN(...) al_print(LogLevel::Warning, __VA_ARGS__)
-#define ERR(...) al_print(LogLevel::Error, gLogFile, __VA_ARGS__)
-#endif
+#define ERR(...) al_print(LogLevel::Error, __VA_ARGS__)
#endif /* CORE_LOGGING_H */
diff --git a/core/mastering.cpp b/core/mastering.cpp
index 97a4008e..4445719b 100644
--- a/core/mastering.cpp
+++ b/core/mastering.cpp
@@ -382,10 +382,10 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa
Compressor::~Compressor()
{
if(mHold)
- al::destroy_at(mHold);
+ std::destroy_at(mHold);
mHold = nullptr;
if(mDelay)
- al::destroy_n(mDelay, mNumChans);
+ std::destroy_n(mDelay, mNumChans);
mDelay = nullptr;
}
diff --git a/core/mixer.cpp b/core/mixer.cpp
index 066c57bd..806ac8b8 100644
--- a/core/mixer.cpp
+++ b/core/mixer.cpp
@@ -82,14 +82,13 @@ std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, c
return coeffs;
}
-void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain,
- const al::span<float,MaxAmbiChannels> gains)
+void ComputePanGains(const MixParams *mix, const al::span<const float,MaxAmbiChannels> coeffs,
+ const float ingain, const al::span<float,MaxAmbiChannels> gains)
{
auto ambimap = mix->AmbiMap.cbegin();
auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), gains.begin(),
[coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> float
- { return chanmap.Scale * coeffs[chanmap.Index] * ingain; }
- );
+ { return chanmap.Scale * coeffs[chanmap.Index] * ingain; });
std::fill(iter, gains.end(), 0.0f);
}
diff --git a/core/mixer.h b/core/mixer.h
index aa7597bb..9062ebac 100644
--- a/core/mixer.h
+++ b/core/mixer.h
@@ -58,7 +58,7 @@ std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, c
* vector must be normalized (unit length), and the spread is the angular width
* of the sound (0...tau).
*/
-inline std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const float (&dir)[3],
+inline std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const al::span<const float,3> dir,
const float spread)
{
/* Convert from OpenAL coords to Ambisonics. */
@@ -71,7 +71,7 @@ inline std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const float (&dir)[
* Calculates ambisonic coefficients based on an OpenAL direction vector. The
* vector must be normalized (unit length).
*/
-constexpr std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const float (&dir)[3])
+constexpr std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const al::span<const float,3> dir)
{
/* Convert from OpenAL coords to Ambisonics. */
return CalcAmbiCoeffs(-dir[0], dir[1], -dir[2]);
@@ -103,7 +103,7 @@ inline std::array<float,MaxAmbiChannels> CalcAngleCoeffs(const float azimuth,
* coeffs are a 'slice' of a transform matrix for the input channel, used to
* scale and orient the sound samples.
*/
-void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain,
- const al::span<float,MaxAmbiChannels> gains);
+void ComputePanGains(const MixParams *mix, const al::span<const float,MaxAmbiChannels> coeffs,
+ const float ingain, const al::span<float,MaxAmbiChannels> gains);
#endif /* CORE_MIXER_H */
diff --git a/core/mixer/mixer_neon.cpp b/core/mixer/mixer_neon.cpp
index ef2936b3..ead775af 100644
--- a/core/mixer/mixer_neon.cpp
+++ b/core/mixer/mixer_neon.cpp
@@ -342,7 +342,7 @@ void Mix_<NEONTag>(const al::span<const float> InSamples, const al::span<FloatBu
{
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto min_len = minz(Counter, InSamples.size());
- const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
+ const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len;
for(FloatBufferLine &output : OutBuffer)
MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++,
@@ -355,7 +355,7 @@ void Mix_<NEONTag>(const al::span<const float> InSamples, float *OutBuffer, floa
{
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto min_len = minz(Counter, InSamples.size());
- const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
+ const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len;
MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len,
aligned_len, Counter);
diff --git a/core/mixer/mixer_sse.cpp b/core/mixer/mixer_sse.cpp
index 0aa5d5fb..70f77c14 100644
--- a/core/mixer/mixer_sse.cpp
+++ b/core/mixer/mixer_sse.cpp
@@ -307,7 +307,7 @@ void Mix_<SSETag>(const al::span<const float> InSamples, const al::span<FloatBuf
{
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto min_len = minz(Counter, InSamples.size());
- const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
+ const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len;
for(FloatBufferLine &output : OutBuffer)
MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++,
@@ -320,7 +320,7 @@ void Mix_<SSETag>(const al::span<const float> InSamples, float *OutBuffer, float
{
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto min_len = minz(Counter, InSamples.size());
- const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
+ const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len;
MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len,
aligned_len, Counter);
diff --git a/core/uhjfilter.cpp b/core/uhjfilter.cpp
index df50956a..28999e09 100644
--- a/core/uhjfilter.cpp
+++ b/core/uhjfilter.cpp
@@ -9,7 +9,9 @@
#include "alcomplex.h"
#include "alnumeric.h"
#include "opthelpers.h"
+#include "pffft.h"
#include "phase_shifter.h"
+#include "vector.h"
UhjQualityType UhjDecodeQuality{UhjQualityType::Default};
@@ -18,38 +20,141 @@ UhjQualityType UhjEncodeQuality{UhjQualityType::Default};
namespace {
-const PhaseShifterT<UhjLength256> PShiftLq{};
-const PhaseShifterT<UhjLength512> PShiftHq{};
+struct PFFFTSetupDeleter {
+ void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); }
+};
+using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>;
+/* Convolution is implemented using a segmented overlap-add method. The filter
+ * response is broken up into multiple segments of 128 samples, and each
+ * segment has an FFT applied with a 256-sample buffer (the latter half left
+ * silent) to get its frequency-domain response.
+ *
+ * Input samples are similarly broken up into 128-sample segments, with a 256-
+ * sample FFT applied to each new incoming segment to get its frequency-domain
+ * response. A history of FFT'd input segments is maintained, equal to the
+ * number of filter response segments.
+ *
+ * To apply the convolution, each filter response segment is convolved with its
+ * paired input segment (using complex multiplies, far cheaper than time-domain
+ * FIRs), accumulating into an FFT buffer. The input history is then shifted to
+ * align with later filter response segments for the next input segment.
+ *
+ * An inverse FFT is then applied to the accumulated FFT buffer to get a 256-
+ * sample time-domain response for output, which is split in two halves. The
+ * first half is the 128-sample output, and the second half is a 128-sample
+ * (really, 127) delayed extension, which gets added to the output next time.
+ * Convolving two time-domain responses of length N results in a time-domain
+ * signal of length N*2 - 1, and this holds true regardless of the convolution
+ * being applied in the frequency domain, so these "overflow" samples need to
+ * be accounted for.
+ */
template<size_t N>
-struct GetPhaseShifter;
-template<>
-struct GetPhaseShifter<UhjLength256> { static auto& Get() noexcept { return PShiftLq; } };
-template<>
-struct GetPhaseShifter<UhjLength512> { static auto& Get() noexcept { return PShiftHq; } };
+struct SegmentedFilter {
+ static constexpr size_t sFftLength{256};
+ static constexpr size_t sSampleLength{sFftLength / 2};
+ static constexpr size_t sNumSegments{N/sSampleLength};
+ static_assert(N >= sFftLength);
+ static_assert((N % sSampleLength) == 0);
+ PFFFTSetupPtr mFft;
+ alignas(16) std::array<float,sFftLength*sNumSegments> mFilterData;
+
+ SegmentedFilter()
+ {
+ mFft = PFFFTSetupPtr{pffft_new_setup(sFftLength, PFFFT_REAL)};
+
+ using complex_d = std::complex<double>;
+ constexpr size_t fft_size{N};
+ constexpr size_t half_size{fft_size / 2};
+
+ /* To set up the filter, we need to generate the desired response.
+ * Start with a pure delay that passes all frequencies through.
+ */
+ auto fftBuffer = std::make_unique<complex_d[]>(fft_size);
+ std::fill_n(fftBuffer.get(), fft_size, complex_d{});
+ fftBuffer[half_size] = 1.0;
+
+ /* Convert to the frequency domain, shift the phase of each bin by +90
+ * degrees, then convert back to the time domain.
+ *
+ * NOTE: The 0- and half-frequency are always real for a real signal.
+ * To maintain that and their phase (0 or pi), they're heavily
+ * attenuated instead of shifted like the others.
+ */
+ forward_fft(al::span{fftBuffer.get(), fft_size});
+ fftBuffer[0] *= std::numeric_limits<double>::epsilon();
+ for(size_t i{1};i < half_size;++i)
+ fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()};
+ fftBuffer[half_size] *= std::numeric_limits<double>::epsilon();
+ for(size_t i{half_size+1};i < fft_size;++i)
+ fftBuffer[i] = std::conj(fftBuffer[fft_size - i]);
+ inverse_fft(al::span{fftBuffer.get(), fft_size});
+
+ /* The segments of the filter are converted back to the frequency
+ * domain, each on their own (0 stuffed).
+ */
+ auto fftBuffer2 = std::make_unique<complex_d[]>(sFftLength);
+ auto fftTmp = al::vector<float,16>(sFftLength);
+ float *filter{mFilterData.data()};
+ for(size_t s{0};s < sNumSegments;++s)
+ {
+ for(size_t i{0};i < sSampleLength;++i)
+ fftBuffer2[i] = fftBuffer[sSampleLength*s + i].real() / double{fft_size};
+ std::fill_n(fftBuffer2.get()+sSampleLength, sSampleLength, complex_d{});
+ forward_fft(al::span{fftBuffer2.get(), sFftLength});
+
+ /* Convert to zdomain data for PFFFT, scaled by the FFT length so
+ * the iFFT result will be normalized.
+ */
+ for(size_t i{0};i < sSampleLength;++i)
+ {
+ fftTmp[i*2 + 0] = static_cast<float>(fftBuffer2[i].real()) / float{sFftLength};
+ fftTmp[i*2 + 1] = static_cast<float>((i == 0) ? fftBuffer2[sSampleLength].real()
+ : fftBuffer2[i].imag()) / float{sFftLength};
+ }
+ pffft_zreorder(mFft.get(), fftTmp.data(), filter, PFFFT_BACKWARD);
+ filter += sFftLength;
+ }
+ }
+};
+
+template<size_t N>
+const SegmentedFilter<N> gSegmentedFilter;
+
+template<size_t N>
+const PhaseShifterT<N> PShifter;
-constexpr float square(float x) noexcept
-{ return x*x; }
/* Filter coefficients for the 'base' all-pass IIR, which applies a frequency-
* dependent phase-shift of N degrees. The output of the filter requires a 1-
* sample delay.
*/
constexpr std::array<float,4> Filter1Coeff{{
- square(0.6923878f), square(0.9360654322959f), square(0.9882295226860f),
- square(0.9987488452737f)
+ 0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f
}};
/* Filter coefficients for the offset all-pass IIR, which applies a frequency-
* dependent phase-shift of N+90 degrees.
*/
constexpr std::array<float,4> Filter2Coeff{{
- square(0.4021921162426f), square(0.8561710882420f), square(0.9722909545651f),
- square(0.9952884791278f)
+ 0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156684f
}};
} // namespace
+void UhjAllPassFilter::processOne(const al::span<const float, 4> coeffs, float x)
+{
+ auto state = mState;
+ for(size_t i{0};i < 4;++i)
+ {
+ const float y{x*coeffs[i] + state[i].z[0]};
+ state[i].z[0] = state[i].z[1];
+ state[i].z[1] = y*coeffs[i] - x;
+ x = y;
+ }
+ mState = state;
+}
+
void UhjAllPassFilter::process(const al::span<const float,4> coeffs,
const al::span<const float> src, const bool updateState, float *RESTRICT dst)
{
@@ -92,7 +197,10 @@ template<size_t N>
void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
const al::span<const float*const,3> InSamples, const size_t SamplesToDo)
{
- const auto &PShift = GetPhaseShifter<N>::Get();
+ static constexpr auto &Filter = gSegmentedFilter<N>;
+ static_assert(sFftLength == Filter.sFftLength);
+ static_assert(sSegmentSize == Filter.sSampleLength);
+ static_assert(sNumSegments == Filter.sNumSegments);
ASSUME(SamplesToDo > 0);
@@ -109,10 +217,71 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
mS[i] = 0.9396926f*mW[i] + 0.1855740f*mX[i];
/* Precompute j(-0.3420201*W + 0.5098604*X) and store in mD. */
- std::transform(winput, winput+SamplesToDo, xinput, mWX.begin() + sWXInOffset,
- [](const float w, const float x) noexcept -> float
- { return -0.3420201f*w + 0.5098604f*x; });
- PShift.process({mD.data(), SamplesToDo}, mWX.data());
+ size_t curseg{mCurrentSegment};
+ for(size_t base{0};base < SamplesToDo;)
+ {
+ const size_t todo{minz(sSegmentSize-mFifoPos, SamplesToDo-base)};
+
+ /* Copy out the samples that were previously processed by the FFT. */
+ std::copy_n(mWXInOut.begin()+mFifoPos, todo, mD.begin()+base);
+
+ /* Transform the non-delayed input and store in the front half of the
+ * filter input.
+ */
+ std::transform(winput+base, winput+base+todo, xinput+base, mWXInOut.begin()+mFifoPos,
+ [](const float w, const float x) noexcept -> float
+ { return -0.3420201f*w + 0.5098604f*x; });
+
+ mFifoPos += todo;
+ base += todo;
+
+ /* Check whether the input buffer is filled with new samples. */
+ if(mFifoPos < sSegmentSize) break;
+ mFifoPos = 0;
+
+ /* Copy the new input to the next history segment, clearing the back
+ * half of the segment, and convert to the frequency domain.
+ */
+ float *input{mWXHistory.data() + curseg*sFftLength};
+ std::copy_n(mWXInOut.begin(), sSegmentSize, input);
+ std::fill_n(input+sSegmentSize, sSegmentSize, 0.0f);
+
+ pffft_transform(Filter.mFft.get(), input, input, mWorkData.data(), PFFFT_FORWARD);
+
+ /* Convolve each input segment with its IR filter counterpart (aligned
+ * in time, from newest to oldest).
+ */
+ mFftBuffer.fill(0.0f);
+ const float *filter{Filter.mFilterData.data()};
+ for(size_t s{curseg};s < sNumSegments;++s)
+ {
+ pffft_zconvolve_accumulate(Filter.mFft.get(), input, filter, mFftBuffer.data());
+ input += sFftLength;
+ filter += sFftLength;
+ }
+ input = mWXHistory.data();
+ for(size_t s{0};s < curseg;++s)
+ {
+ pffft_zconvolve_accumulate(Filter.mFft.get(), input, filter, mFftBuffer.data());
+ input += sFftLength;
+ filter += sFftLength;
+ }
+
+ /* Convert back to samples, writing to the output and storing the extra
+ * for next time.
+ */
+ pffft_transform(Filter.mFft.get(), mFftBuffer.data(), mFftBuffer.data(),
+ mWorkData.data(), PFFFT_BACKWARD);
+
+ for(size_t i{0};i < sSegmentSize;++i)
+ mWXInOut[i] = mFftBuffer[i] + mWXInOut[sSegmentSize+i];
+ for(size_t i{0};i < sSegmentSize;++i)
+ mWXInOut[sSegmentSize+i] = mFftBuffer[sSegmentSize+i];
+
+ /* Shift the input history. */
+ curseg = curseg ? (curseg-1) : (sNumSegments-1);
+ }
+ mCurrentSegment = curseg;
/* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y */
for(size_t i{0};i < SamplesToDo;++i)
@@ -122,7 +291,6 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
std::copy(mW.cbegin()+SamplesToDo, mW.cbegin()+SamplesToDo+sFilterDelay, mW.begin());
std::copy(mX.cbegin()+SamplesToDo, mX.cbegin()+SamplesToDo+sFilterDelay, mX.begin());
std::copy(mY.cbegin()+SamplesToDo, mY.cbegin()+SamplesToDo+sFilterDelay, mY.begin());
- std::copy(mWX.cbegin()+SamplesToDo, mWX.cbegin()+SamplesToDo+sWXInOffset, mWX.begin());
/* Apply a delay to the existing output to align with the input delay. */
auto *delayBuffer = mDirectDelay.data();
@@ -133,7 +301,7 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
float *inout{al::assume_aligned<16>(buffer)};
auto inout_end = inout + SamplesToDo;
- if(SamplesToDo >= sFilterDelay) LIKELY
+ if(SamplesToDo >= sFilterDelay)
{
auto delay_end = std::rotate(inout, inout_end - sFilterDelay, inout_end);
std::swap_ranges(inout, delay_end, distbuf);
@@ -240,7 +408,7 @@ void UhjDecoder<N>::decode(const al::span<float*> samples, const size_t samplesT
{
static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large");
- const auto &PShift = GetPhaseShifter<N>::Get();
+ constexpr auto &PShift = PShifter<N>;
ASSUME(samplesToDo > 0);
@@ -313,11 +481,11 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
const float *RESTRICT right{al::assume_aligned<16>(samples[1])};
/* S = Left + Right */
- for(size_t i{0};i < samplesToDo;++i)
+ for(size_t i{0};i < samplesToDo+sInputPadding;++i)
mS[i] = left[i] + right[i];
/* D = Left - Right */
- for(size_t i{0};i < samplesToDo;++i)
+ for(size_t i{0};i < samplesToDo+sInputPadding;++i)
mD[i] = left[i] - right[i];
}
@@ -326,14 +494,13 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
float *RESTRICT youtput{al::assume_aligned<16>(samples[2])};
/* Precompute j(0.828331*D + 0.767820*T) and store in xoutput. */
- std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput, mTemp.begin(),
+ std::transform(mD.cbegin(), mD.cbegin()+sInputPadding+samplesToDo, youtput, mTemp.begin(),
[](const float d, const float t) noexcept { return 0.828331f*d + 0.767820f*t; });
- mFilter2DT.process(Filter2Coeff, {mTemp.data(), samplesToDo}, updateState, xoutput);
+ if(mFirstRun) mFilter2DT.processOne(Filter2Coeff, mTemp[0]);
+ mFilter2DT.process(Filter2Coeff, {mTemp.data()+1, samplesToDo}, updateState, xoutput);
/* Apply filter1 to S and store in mTemp. */
- mTemp[0] = mDelayS;
- mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayS = mTemp[samplesToDo];
+ mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data());
/* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */
for(size_t i{0};i < samplesToDo;++i)
@@ -346,12 +513,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, youtput,
[](const float d, const float t) noexcept { return 0.795968f*d - 0.676392f*t; });
- mTemp[0] = mDelayDT;
- mFilter1DT.process(Filter1Coeff, {youtput, samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayDT = mTemp[samplesToDo];
+ mFilter1DT.process(Filter1Coeff, {youtput, samplesToDo}, updateState, mTemp.data());
/* Precompute j*S and store in youtput. */
- mFilter2S.process(Filter2Coeff, {mS.data(), samplesToDo}, updateState, youtput);
+ if(mFirstRun) mFilter2S.processOne(Filter2Coeff, mS[0]);
+ mFilter2S.process(Filter2Coeff, {mS.data()+1, samplesToDo}, updateState, youtput);
/* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */
for(size_t i{0};i < samplesToDo;++i)
@@ -363,14 +529,14 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
float *RESTRICT zoutput{al::assume_aligned<16>(samples[3])};
/* Apply filter1 to Q and store in mTemp. */
- mTemp[0] = mDelayQ;
- mFilter1Q.process(Filter1Coeff, {zoutput, samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayQ = mTemp[samplesToDo];
+ mFilter1Q.process(Filter1Coeff, {zoutput, samplesToDo}, updateState, mTemp.data());
/* Z = 1.023332*Q */
for(size_t i{0};i < samplesToDo;++i)
zoutput[i] = 1.023332f*mTemp[i];
}
+
+ mFirstRun = false;
}
@@ -392,7 +558,7 @@ void UhjStereoDecoder<N>::decode(const al::span<float*> samples, const size_t sa
{
static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large");
- const auto &PShift = GetPhaseShifter<N>::Get();
+ constexpr auto &PShift = PShifter<N>;
ASSUME(samplesToDo > 0);
@@ -470,7 +636,7 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
const float *RESTRICT left{al::assume_aligned<16>(samples[0])};
const float *RESTRICT right{al::assume_aligned<16>(samples[1])};
- for(size_t i{0};i < samplesToDo;++i)
+ for(size_t i{0};i < samplesToDo+sInputPadding;++i)
mS[i] = left[i] + right[i];
/* Pre-apply the width factor to the difference signal D. Smoothly
@@ -480,7 +646,7 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
const float wcurrent{(mCurrentWidth < 0.0f) ? wtarget : mCurrentWidth};
if(wtarget == wcurrent || !updateState)
{
- for(size_t i{0};i < samplesToDo;++i)
+ for(size_t i{0};i < samplesToDo+sInputPadding;++i)
mD[i] = (left[i] - right[i]) * wcurrent;
mCurrentWidth = wcurrent;
}
@@ -493,6 +659,8 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
mD[i] = (left[i] - right[i]) * (wcurrent + wstep*fi);
fi += 1.0f;
}
+ for(size_t i{samplesToDo};i < samplesToDo+sInputPadding;++i)
+ mD[i] = (left[i] - right[i]) * wtarget;
mCurrentWidth = wtarget;
}
}
@@ -502,12 +670,11 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
float *RESTRICT youtput{al::assume_aligned<16>(samples[2])};
/* Apply filter1 to S and store in mTemp. */
- mTemp[0] = mDelayS;
- mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayS = mTemp[samplesToDo];
+ mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data());
/* Precompute j*D and store in xoutput. */
- mFilter2D.process(Filter2Coeff, {mD.data(), samplesToDo}, updateState, xoutput);
+ if(mFirstRun) mFilter2D.processOne(Filter2Coeff, mD[0]);
+ mFilter2D.process(Filter2Coeff, {mD.data()+1, samplesToDo}, updateState, xoutput);
/* W = 0.6098637*S - 0.6896511*j*w*D */
for(size_t i{0};i < samplesToDo;++i)
@@ -517,16 +684,17 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
xoutput[i] = 0.8624776f*mTemp[i] + 0.7626955f*xoutput[i];
/* Precompute j*S and store in youtput. */
- mFilter2S.process(Filter2Coeff, {mS.data(), samplesToDo}, updateState, youtput);
+ if(mFirstRun) mFilter2S.processOne(Filter2Coeff, mS[0]);
+ mFilter2S.process(Filter2Coeff, {mS.data()+1, samplesToDo}, updateState, youtput);
/* Apply filter1 to D and store in mTemp. */
- mTemp[0] = mDelayD;
- mFilter1D.process(Filter1Coeff, {mD.data(), samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayD = mTemp[samplesToDo];
+ mFilter1D.process(Filter1Coeff, {mD.data(), samplesToDo}, updateState, mTemp.data());
/* Y = 1.6822415*w*D - 0.2156194*j*S */
for(size_t i{0};i < samplesToDo;++i)
youtput[i] = 1.6822415f*mTemp[i] - 0.2156194f*youtput[i];
+
+ mFirstRun = false;
}
diff --git a/core/uhjfilter.h b/core/uhjfilter.h
index df308094..348dc7e1 100644
--- a/core/uhjfilter.h
+++ b/core/uhjfilter.h
@@ -29,6 +29,7 @@ struct UhjAllPassFilter {
};
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, float *RESTRICT dst);
};
@@ -50,7 +51,10 @@ struct UhjEncoderBase {
template<size_t N>
struct UhjEncoder final : public UhjEncoderBase {
- static constexpr size_t sFilterDelay{N/2};
+ static constexpr size_t sFftLength{256};
+ static constexpr size_t sSegmentSize{sFftLength/2};
+ static constexpr size_t sNumSegments{N/sSegmentSize};
+ static constexpr size_t sFilterDelay{N/2 + sSegmentSize};
/* Delays and processing storage for the input signal. */
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mW{};
@@ -60,11 +64,12 @@ struct UhjEncoder final : public UhjEncoderBase {
alignas(16) std::array<float,BufferLineSize> mS{};
alignas(16) std::array<float,BufferLineSize> mD{};
- /* History and temp storage for the FIR filter. New samples should be
- * written to index sFilterDelay*2 - 1.
- */
- static constexpr size_t sWXInOffset{sFilterDelay*2 - 1};
- alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mWX{};
+ /* History and temp storage for the convolution filter. */
+ size_t mFifoPos{}, mCurrentSegment{};
+ alignas(16) std::array<float,sFftLength> mWXInOut{};
+ alignas(16) std::array<float,sFftLength> mFftBuffer{};
+ alignas(16) std::array<float,sFftLength> mWorkData{};
+ alignas(16) std::array<float,sFftLength*sNumSegments> mWXHistory{};
alignas(16) std::array<std::array<float,sFilterDelay>,2> mDirectDelay{};
@@ -77,8 +82,6 @@ struct UhjEncoder final : public UhjEncoderBase {
*/
void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples,
const size_t SamplesToDo) override;
-
- DEF_NEWDEL(UhjEncoder)
};
struct UhjEncoderIIR final : public UhjEncoderBase {
@@ -160,17 +163,18 @@ struct UhjDecoder final : public DecoderBase {
};
struct UhjDecoderIIR final : public DecoderBase {
- /* FIXME: These IIR decoder filters actually have a 1-sample delay on the
- * non-filtered components, which is not reflected in the source latency
- * value. sInputPadding is 0, however, because it doesn't need any extra
- * input samples.
+ /* These IIR decoder filters normally have a 1-sample delay on the non-
+ * filtered components. However, the filtered components are made to skip
+ * the first output sample and take one future sample, which puts it ahead
+ * by one sample. The first filtered output sample is cut to align it with
+ * the first non-filtered sample, similar to the FIR filters.
*/
- static constexpr size_t sInputPadding{0};
+ static constexpr size_t sInputPadding{1};
- alignas(16) std::array<float,BufferLineSize> mS{};
- alignas(16) std::array<float,BufferLineSize> mD{};
- alignas(16) std::array<float,BufferLineSize+1> mTemp{};
- float mDelayS{}, mDelayDT{}, mDelayQ{};
+ bool mFirstRun{true};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mTemp{};
UhjAllPassFilter mFilter1S;
UhjAllPassFilter mFilter2DT;
@@ -211,14 +215,14 @@ struct UhjStereoDecoder final : public DecoderBase {
};
struct UhjStereoDecoderIIR final : public DecoderBase {
- static constexpr size_t sInputPadding{0};
+ static constexpr size_t sInputPadding{1};
+ bool mFirstRun{true};
float mCurrentWidth{-1.0f};
- alignas(16) std::array<float,BufferLineSize> mS{};
- alignas(16) std::array<float,BufferLineSize> mD{};
- alignas(16) std::array<float,BufferLineSize+1> mTemp{};
- float mDelayS{}, mDelayD{};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
+ alignas(16) std::array<float,BufferLineSize> mTemp{};
UhjAllPassFilter mFilter1S;
UhjAllPassFilter mFilter2D;
diff --git a/core/uiddefs.cpp b/core/uiddefs.cpp
index 244c01a5..9471bba5 100644
--- a/core/uiddefs.cpp
+++ b/core/uiddefs.cpp
@@ -19,12 +19,8 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x
DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
-DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
-DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
-DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
-DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
-#ifdef HAVE_WASAPI
+#if defined(HAVE_WASAPI) && !defined(ALSOFT_UWP)
#include <wtypes.h>
#include <devpropdef.h>
#include <propkeydef.h>
diff --git a/core/voice.cpp b/core/voice.cpp
index e8fbcccd..3889c42d 100644
--- a/core/voice.cpp
+++ b/core/voice.cpp
@@ -12,13 +12,12 @@
#include <iterator>
#include <memory>
#include <new>
+#include <optional>
#include <stdlib.h>
#include <utility>
#include <vector>
-#include "albyte.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "alstring.h"
#include "ambidefs.h"
@@ -129,7 +128,7 @@ inline HrtfMixerBlendFunc SelectHrtfBlendMixer()
} // namespace
-void Voice::InitMixer(al::optional<std::string> resampler)
+void Voice::InitMixer(std::optional<std::string> resampler)
{
if(resampler)
{
@@ -227,10 +226,9 @@ void SendSourceStoppedEvent(ContextBase *context, uint id)
auto evt_vec = ring->getWriteVector();
if(evt_vec.first.len < 1) return;
- AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf),
- AsyncEvent::SourceStateChange)};
- evt->u.srcstate.id = id;
- evt->u.srcstate.state = AsyncEvent::SrcState::Stop;
+ auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec.first.buf);
+ evt.mId = id;
+ evt.mState = AsyncSrcState::Stop;
ring->writeAdvance(1);
}
@@ -264,7 +262,7 @@ const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *ds
template<FmtType Type>
-inline void LoadSamples(float *RESTRICT dstSamples, const al::byte *src, const size_t srcChan,
+inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *src, const size_t srcChan,
const size_t srcOffset, const size_t srcStep, const size_t /*samplesPerBlock*/,
const size_t samplesToLoad) noexcept
{
@@ -275,7 +273,7 @@ inline void LoadSamples(float *RESTRICT dstSamples, const al::byte *src, const s
}
template<>
-inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src,
+inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const std::byte *src,
const size_t srcChan, const size_t srcOffset, const size_t srcStep,
const size_t samplesPerBlock, const size_t samplesToLoad) noexcept
{
@@ -289,14 +287,15 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
/* NOTE: This could probably be optimized better. */
size_t wrote{0};
do {
+ static constexpr int MaxStepIndex{static_cast<int>(std::size(IMAStep_size)) - 1};
/* 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{src[srcChan*4] | (src[srcChan*4 + 1] << 8)};
- int index{src[srcChan*4 + 2] | (src[srcChan*4 + 3] << 8)};
+ int sample{int(src[srcChan*4]) | (int(src[srcChan*4 + 1]) << 8)};
+ int index{int(src[srcChan*4 + 2]) | (int(src[srcChan*4 + 3]) << 8)};
sample = (sample^0x8000) - 32768;
- index = clampi((index^0x8000) - 32768, 0, al::size(IMAStep_size)-1);
+ index = clampi((index^0x8000) - 32768, 0, MaxStepIndex);
if(skip == 0)
{
@@ -312,7 +311,7 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
sample = clampi(sample, -32768, 32767);
index += IMA4Index_adjust[nibble];
- index = clampi(index, 0, al::size(IMAStep_size)-1);
+ index = clampi(index, 0, MaxStepIndex);
return sample;
};
@@ -325,17 +324,17 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
* always be less than the block size). They need to be decoded despite
* being ignored for proper state on the remaining samples.
*/
- const al::byte *nibbleData{src + (srcStep+srcChan)*4};
+ const std::byte *nibbleData{src + (srcStep+srcChan)*4};
size_t nibbleOffset{0};
const size_t startOffset{skip + 1};
for(;skip;--skip)
{
const size_t byteShift{(nibbleOffset&1) * 4};
- const size_t wordOffset{(nibbleOffset>>1) & ~size_t{3}};
+ const size_t wordOffset{(nibbleOffset>>1) & ~3_uz};
const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)};
++nibbleOffset;
- std::ignore = decode_sample((nibbleData[byteOffset]>>byteShift) & 15u);
+ std::ignore = decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u);
}
/* Second, decode the rest of the block and write to the output, until
@@ -345,11 +344,11 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
for(size_t i{0};i < todo;++i)
{
const size_t byteShift{(nibbleOffset&1) * 4};
- const size_t wordOffset{(nibbleOffset>>1) & ~size_t{3}};
+ const size_t wordOffset{(nibbleOffset>>1) & ~3_uz};
const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)};
++nibbleOffset;
- const int result{decode_sample((nibbleData[byteOffset]>>byteShift) & 15u)};
+ const int result{decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u)};
dstSamples[wrote++] = static_cast<float>(result) / 32768.0f;
}
if(wrote == samplesToLoad)
@@ -360,7 +359,7 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
}
template<>
-inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *src,
+inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const std::byte *src,
const size_t srcChan, const size_t srcOffset, const size_t srcStep,
const size_t samplesPerBlock, const size_t samplesToLoad) noexcept
{
@@ -377,19 +376,19 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *
* nibble sample value. This is followed by the two initial 16-bit
* sample history values.
*/
- const al::byte *input{src};
- const uint8_t blockpred{std::min(input[srcChan], uint8_t{6})};
+ const std::byte *input{src};
+ const uint8_t blockpred{std::min(uint8_t(input[srcChan]), uint8_t{6})};
input += srcStep;
- int delta{input[2*srcChan + 0] | (input[2*srcChan + 1] << 8)};
+ int delta{int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1]) << 8)};
input += srcStep*2;
int sampleHistory[2]{};
- sampleHistory[0] = input[2*srcChan + 0] | (input[2*srcChan + 1]<<8);
+ sampleHistory[0] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8);
input += srcStep*2;
- sampleHistory[1] = input[2*srcChan + 0] | (input[2*srcChan + 1]<<8);
+ sampleHistory[1] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8);
input += srcStep*2;
- const auto coeffs = al::as_span(MSADPCMAdaptionCoeff[blockpred]);
+ const al::span coeffs{MSADPCMAdaptionCoeff[blockpred]};
delta = (delta^0x8000) - 32768;
sampleHistory[0] = (sampleHistory[0]^0x8000) - 32768;
sampleHistory[1] = (sampleHistory[1]^0x8000) - 32768;
@@ -439,7 +438,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *
const size_t byteShift{((nibbleOffset&1)^1) * 4};
nibbleOffset += srcStep;
- std::ignore = decode_sample((input[byteOffset]>>byteShift) & 15);
+ std::ignore = decode_sample(int(input[byteOffset]>>byteShift) & 15);
}
/* Now decode the rest of the block, until the end of the block or the
@@ -452,7 +451,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *
const size_t byteShift{((nibbleOffset&1)^1) * 4};
nibbleOffset += srcStep;
- const int sample{decode_sample((input[byteOffset]>>byteShift) & 15)};
+ const int sample{decode_sample(int(input[byteOffset]>>byteShift) & 15)};
dstSamples[wrote++] = static_cast<float>(sample) / 32768.0f;
}
if(wrote == samplesToLoad)
@@ -462,7 +461,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *
} while(true);
}
-void LoadSamples(float *dstSamples, const al::byte *src, const size_t srcChan,
+void LoadSamples(float *dstSamples, const std::byte *src, const size_t srcChan,
const size_t srcOffset, const FmtType srcType, const size_t srcStep,
const size_t samplesPerBlock, const size_t samplesToLoad) noexcept
{
@@ -475,6 +474,7 @@ void LoadSamples(float *dstSamples, const al::byte *src, const size_t srcChan,
{
HANDLE_FMT(FmtUByte);
HANDLE_FMT(FmtShort);
+ HANDLE_FMT(FmtInt);
HANDLE_FMT(FmtFloat);
HANDLE_FMT(FmtDouble);
HANDLE_FMT(FmtMulaw);
@@ -798,7 +798,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
using ResBufType = decltype(DeviceBase::mResampleData);
static constexpr uint srcSizeMax{static_cast<uint>(ResBufType{}.size()-MaxResamplerEdge)};
- const auto prevSamples = al::as_span(mPrevSamples[chan]);
+ const al::span prevSamples{mPrevSamples[chan]};
const auto resampleBuffer = std::copy(prevSamples.cbegin(), prevSamples.cend(),
Device->mResampleData.begin()) - MaxResamplerEdge;
int intPos{DataPosInt};
@@ -1101,7 +1101,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
{
const size_t byteOffset{blocksDone*mBytesPerBlock};
const size_t byteEnd{mNumCallbackBlocks*mBytesPerBlock};
- al::byte *data{BufferListItem->mSamples};
+ std::byte *data{BufferListItem->mSamples};
std::copy(data+byteOffset, data+byteEnd, data);
mNumCallbackBlocks -= blocksDone;
mCallbackBlockBase += blocksDone;
@@ -1145,16 +1145,15 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
/* Send any events now, after the position/buffer info was updated. */
const auto enabledevt = Context->mEnabledEvts.load(std::memory_order_acquire);
- if(buffers_done > 0 && enabledevt.test(AsyncEvent::BufferCompleted))
+ if(buffers_done > 0 && enabledevt.test(al::to_underlying(AsyncEnableBits::BufferCompleted)))
{
RingBuffer *ring{Context->mAsyncEvents.get()};
auto evt_vec = ring->getWriteVector();
if(evt_vec.first.len > 0)
{
- AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf),
- AsyncEvent::BufferCompleted)};
- evt->u.bufcomp.id = SourceID;
- evt->u.bufcomp.count = buffers_done;
+ auto &evt = InitAsyncEvent<AsyncBufferCompleteEvent>(evt_vec.first.buf);
+ evt.mId = SourceID;
+ evt.mCount = buffers_done;
ring->writeAdvance(1);
}
}
@@ -1165,7 +1164,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
* ensures any residual noise fades to 0 amplitude.
*/
mPlayState.store(Stopping, std::memory_order_release);
- if(enabledevt.test(AsyncEvent::SourceStateChange))
+ if(enabledevt.test(al::to_underlying(AsyncEnableBits::SourceState)))
SendSourceStoppedEvent(Context, SourceID);
}
}
@@ -1275,7 +1274,7 @@ void Voice::prepare(DeviceBase *device)
else if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder)
{
const uint8_t *OrderFromChan{Is2DAmbisonic(mFmtChannels) ?
- AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()};
+ AmbiIndex::OrderFrom2DChannel.data() : AmbiIndex::OrderFromChannel.data()};
const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder,
device->m2DMixing);
diff --git a/core/voice.h b/core/voice.h
index 57ee7b01..a599eda8 100644
--- a/core/voice.h
+++ b/core/voice.h
@@ -5,13 +5,12 @@
#include <atomic>
#include <bitset>
#include <chrono>
+#include <cstddef>
#include <memory>
-#include <stddef.h>
+#include <optional>
#include <string>
-#include "albyte.h"
#include "almalloc.h"
-#include "aloptional.h"
#include "alspan.h"
#include "bufferline.h"
#include "buffer_storage.h"
@@ -100,7 +99,7 @@ struct VoiceBufferItem {
uint mLoopStart{0u};
uint mLoopEnd{0u};
- al::byte *mSamples{nullptr};
+ std::byte *mSamples{nullptr};
};
@@ -270,7 +269,7 @@ struct Voice {
void prepare(DeviceBase *device);
- static void InitMixer(al::optional<std::string> resampler);
+ static void InitMixer(std::optional<std::string> resampler);
DEF_NEWDEL(Voice)
};