aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorSven Göthel <[email protected]>2024-01-05 13:52:12 +0100
committerSven Göthel <[email protected]>2024-01-05 13:52:12 +0100
commitec98cdacc85ff0202852472c7756586437912f22 (patch)
tree42414746a27ab35cb8cdbc95af521d74821e57f4 /core
parentfd5269bec9a5fe4815974b1786a037e6a247bfd2 (diff)
parentb82cd2e60edb8fbe5fdd3567105ae76a016a554c (diff)
Merge remote-tracking branch 'upstream/master'HEADmaster
Diffstat (limited to 'core')
-rw-r--r--core/ambdec.cpp37
-rw-r--r--core/ambdec.h10
-rw-r--r--core/async_event.h3
-rw-r--r--core/bformatdec.cpp16
-rw-r--r--core/bformatdec.h10
-rw-r--r--core/bs2b.cpp127
-rw-r--r--core/bs2b.h81
-rw-r--r--core/bsinc_tables.cpp19
-rw-r--r--core/bsinc_tables.h6
-rw-r--r--core/buffer_storage.cpp2
-rw-r--r--core/buffer_storage.h10
-rw-r--r--core/context.cpp144
-rw-r--r--core/context.h38
-rw-r--r--core/converter.cpp63
-rw-r--r--core/converter.h17
-rw-r--r--core/cubic_defs.h6
-rw-r--r--core/cubic_tables.cpp4
-rw-r--r--core/dbus_wrap.cpp20
-rw-r--r--core/devformat.h3
-rw-r--r--core/device.cpp11
-rw-r--r--core/device.h111
-rw-r--r--core/effects/base.h269
-rw-r--r--core/effectslot.cpp8
-rw-r--r--core/effectslot.h5
-rw-r--r--core/except.cpp2
-rw-r--r--core/except.h7
-rw-r--r--core/filters/biquad.cpp4
-rw-r--r--core/filters/biquad.h4
-rw-r--r--core/filters/nfc.cpp12
-rw-r--r--core/filters/nfc.h25
-rw-r--r--core/fmt_traits.cpp8
-rw-r--r--core/fmt_traits.h5
-rw-r--r--core/fpu_ctrl.cpp21
-rw-r--r--core/fpu_ctrl.h23
-rw-r--r--core/front_stablizer.h1
-rw-r--r--core/helpers.cpp4
-rw-r--r--core/helpers.h4
-rw-r--r--core/hrtf.cpp142
-rw-r--r--core/hrtf.h21
-rw-r--r--core/logging.h4
-rw-r--r--core/mastering.cpp237
-rw-r--r--core/mastering.h27
-rw-r--r--core/mixer/defs.h21
-rw-r--r--core/mixer/mixer_c.cpp68
-rw-r--r--core/mixer/mixer_neon.cpp41
-rw-r--r--core/mixer/mixer_sse.cpp28
-rw-r--r--core/mixer/mixer_sse2.cpp4
-rw-r--r--core/mixer/mixer_sse41.cpp4
-rw-r--r--core/rtkit.cpp4
-rw-r--r--core/uhjfilter.cpp37
-rw-r--r--core/uhjfilter.h12
-rw-r--r--core/voice.cpp98
-rw-r--r--core/voice.h65
-rw-r--r--core/voice_change.h4
54 files changed, 1037 insertions, 920 deletions
diff --git a/core/ambdec.cpp b/core/ambdec.cpp
index f98e1098..f3fe7566 100644
--- a/core/ambdec.cpp
+++ b/core/ambdec.cpp
@@ -42,8 +42,8 @@ enum class ReaderScope {
HFMatrix,
};
-#ifdef __USE_MINGW_ANSI_STDIO
-[[gnu::format(gnu_printf,2,3)]]
+#ifdef __MINGW32__
+[[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]]
#else
[[gnu::format(printf,2,3)]]
#endif
@@ -53,7 +53,7 @@ std::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
auto &str = ret.emplace();
str.resize(256);
- int printed{std::snprintf(const_cast<char*>(str.data()), str.length(), "Line %zu: ", linenum)};
+ int printed{std::snprintf(str.data(), str.length(), "Line %zu: ", linenum)};
if(printed < 0) printed = 0;
auto plen = std::min(static_cast<size_t>(printed), str.length());
@@ -111,7 +111,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
{
if(command == "add_spkr")
{
- if(speaker_pos == NumSpeakers)
+ if(speaker_pos == Speakers.size())
return make_error(linenum, "Too many speakers specified");
AmbDecConf::SpeakerConf &spkr = Speakers[speaker_pos++];
@@ -145,7 +145,7 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
}
else if(command == "add_row")
{
- if(pos == NumSpeakers)
+ if(pos == Speakers.size())
return make_error(linenum, "Too many matrix rows specified");
unsigned int mask{ChanMask};
@@ -205,12 +205,13 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
}
else if(command == "/dec/speakers")
{
- if(NumSpeakers)
+ if(!Speakers.empty())
return make_error(linenum, "Duplicate speakers");
- istr >> NumSpeakers;
- if(!NumSpeakers)
- return make_error(linenum, "Invalid speakers: %zu", NumSpeakers);
- Speakers = std::make_unique<SpeakerConf[]>(NumSpeakers);
+ size_t numspeakers{};
+ istr >> numspeakers;
+ if(!numspeakers)
+ return make_error(linenum, "Invalid speakers: %zu", numspeakers);
+ Speakers.resize(numspeakers);
}
else if(command == "/dec/coeff_scale")
{
@@ -243,22 +244,22 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
}
else if(command == "/speakers/{")
{
- if(!NumSpeakers)
+ if(Speakers.empty())
return make_error(linenum, "Speakers defined without a count");
scope = ReaderScope::Speakers;
}
else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{")
{
- if(!NumSpeakers)
+ if(Speakers.empty())
return make_error(linenum, "Matrix defined without a speaker count");
if(!ChanMask)
return make_error(linenum, "Matrix defined without a channel mask");
- if(!Matrix)
+ if(Matrix.empty())
{
- Matrix = std::make_unique<CoeffArray[]>(NumSpeakers * FreqBands);
- LFMatrix = Matrix.get();
- HFMatrix = LFMatrix + NumSpeakers*(FreqBands-1);
+ Matrix.resize(Speakers.size() * FreqBands);
+ LFMatrix = Matrix.data();
+ HFMatrix = LFMatrix + Speakers.size()*(FreqBands-1);
}
if(FreqBands == 1)
@@ -285,8 +286,8 @@ std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
if(!is_at_end(buffer, endpos))
return make_error(linenum, "Extra junk on end: %s", buffer.substr(endpos).c_str());
- if(speaker_pos < NumSpeakers || hfmatrix_pos < NumSpeakers
- || (FreqBands == 2 && lfmatrix_pos < NumSpeakers))
+ if(speaker_pos < Speakers.size() || hfmatrix_pos < Speakers.size()
+ || (FreqBands == 2 && lfmatrix_pos < Speakers.size()))
return make_error(linenum, "Incomplete decoder definition");
if(CoeffScale == AmbDecScale::Unset)
return make_error(linenum, "No coefficient scaling defined");
diff --git a/core/ambdec.h b/core/ambdec.h
index 19f68697..4305070f 100644
--- a/core/ambdec.h
+++ b/core/ambdec.h
@@ -5,6 +5,7 @@
#include <memory>
#include <optional>
#include <string>
+#include <vector>
#include "core/ambidefs.h"
@@ -34,17 +35,16 @@ struct AmbDecConf {
float Elevation{0.0f};
std::string Connection;
};
- size_t NumSpeakers{0};
- std::unique_ptr<SpeakerConf[]> Speakers;
+ std::vector<SpeakerConf> Speakers;
using CoeffArray = std::array<float,MaxAmbiChannels>;
- std::unique_ptr<CoeffArray[]> Matrix;
+ std::vector<CoeffArray> Matrix;
/* Unused when FreqBands == 1 */
- float LFOrderGain[MaxAmbiOrder+1]{};
+ std::array<float,MaxAmbiOrder+1> LFOrderGain{};
CoeffArray *LFMatrix;
- float HFOrderGain[MaxAmbiOrder+1]{};
+ std::array<float,MaxAmbiOrder+1> HFOrderGain{};
CoeffArray *HFMatrix;
~AmbDecConf();
diff --git a/core/async_event.h b/core/async_event.h
index f1ca0c7b..20857c9c 100644
--- a/core/async_event.h
+++ b/core/async_event.h
@@ -1,6 +1,7 @@
#ifndef CORE_EVENT_H
#define CORE_EVENT_H
+#include <array>
#include <stdint.h>
#include <variant>
@@ -39,7 +40,7 @@ struct AsyncBufferCompleteEvent {
};
struct AsyncDisconnectEvent {
- char msg[244];
+ std::array<char,244> msg;
};
struct AsyncEffectReleaseEvent {
diff --git a/core/bformatdec.cpp b/core/bformatdec.cpp
index a308e185..d6a44799 100644
--- a/core/bformatdec.cpp
+++ b/core/bformatdec.cpp
@@ -36,7 +36,7 @@ BFormatDec::BFormatDec(const size_t inchans, const al::span<const ChannelDec> co
auto &decoder = mChannelDec.emplace<std::vector<ChannelDecoderSingle>>(inchans);
for(size_t j{0};j < decoder.size();++j)
{
- float *outcoeffs{decoder[j].mGains};
+ float *outcoeffs{decoder[j].mGains.data()};
for(const ChannelDec &incoeffs : coeffs)
*(outcoeffs++) = incoeffs[j];
}
@@ -50,11 +50,11 @@ BFormatDec::BFormatDec(const size_t inchans, const al::span<const ChannelDec> co
for(size_t j{0};j < decoder.size();++j)
{
- float *outcoeffs{decoder[j].mGains[sHFBand]};
+ float *outcoeffs{decoder[j].mGains[sHFBand].data()};
for(const ChannelDec &incoeffs : coeffs)
*(outcoeffs++) = incoeffs[j];
- outcoeffs = decoder[j].mGains[sLFBand];
+ outcoeffs = decoder[j].mGains[sLFBand].data();
for(const ChannelDec &incoeffs : coeffslf)
*(outcoeffs++) = incoeffs[j];
}
@@ -76,8 +76,10 @@ void BFormatDec::process(const al::span<FloatBufferLine> OutBuffer,
{
chandec.mXOver.process({input->data(), SamplesToDo}, hfSamples.data(),
lfSamples.data());
- MixSamples(hfSamples, OutBuffer, chandec.mGains[sHFBand], chandec.mGains[sHFBand],0,0);
- MixSamples(lfSamples, OutBuffer, chandec.mGains[sLFBand], chandec.mGains[sLFBand],0,0);
+ MixSamples(hfSamples, OutBuffer, chandec.mGains[sHFBand].data(),
+ chandec.mGains[sHFBand].data(), 0, 0);
+ MixSamples(lfSamples, OutBuffer, chandec.mGains[sLFBand].data(),
+ chandec.mGains[sLFBand].data(), 0, 0);
++input;
}
};
@@ -86,8 +88,8 @@ void BFormatDec::process(const al::span<FloatBufferLine> OutBuffer,
auto *input = InSamples;
for(auto &chandec : decoder)
{
- MixSamples({input->data(), SamplesToDo}, OutBuffer, chandec.mGains, chandec.mGains,
- 0, 0);
+ MixSamples({input->data(), SamplesToDo}, OutBuffer, chandec.mGains.data(),
+ chandec.mGains.data(), 0, 0);
++input;
}
};
diff --git a/core/bformatdec.h b/core/bformatdec.h
index 3bb7f544..8513db03 100644
--- a/core/bformatdec.h
+++ b/core/bformatdec.h
@@ -25,15 +25,15 @@ class BFormatDec {
static constexpr size_t sNumBands{2};
struct ChannelDecoderSingle {
- float mGains[MAX_OUTPUT_CHANNELS];
+ std::array<float,MaxOutputChannels> mGains{};
};
struct ChannelDecoderDual {
BandSplitter mXOver;
- float mGains[sNumBands][MAX_OUTPUT_CHANNELS];
+ std::array<std::array<float,MaxOutputChannels>,sNumBands> mGains{};
};
- alignas(16) std::array<FloatBufferLine,2> mSamples;
+ alignas(16) std::array<FloatBufferLine,2> mSamples{};
const std::unique_ptr<FrontStablizer> mStablizer;
@@ -44,7 +44,7 @@ public:
const al::span<const ChannelDec> coeffslf, const float xover_f0norm,
std::unique_ptr<FrontStablizer> stablizer);
- bool hasStablizer() const noexcept { return mStablizer != nullptr; }
+ [[nodiscard]] auto hasStablizer() const noexcept -> bool { return mStablizer != nullptr; }
/* Decodes the ambisonic input to the given output channels. */
void process(const al::span<FloatBufferLine> OutBuffer, const FloatBufferLine *InSamples,
@@ -58,8 +58,6 @@ public:
static std::unique_ptr<BFormatDec> Create(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);
-
- DEF_NEWDEL(BFormatDec)
};
#endif /* CORE_BFORMATDEC_H */
diff --git a/core/bs2b.cpp b/core/bs2b.cpp
index 303bf9bd..9157c4d7 100644
--- a/core/bs2b.cpp
+++ b/core/bs2b.cpp
@@ -26,72 +26,74 @@
#include <algorithm>
#include <cmath>
#include <iterator>
+#include <stdexcept>
#include "alnumbers.h"
#include "bs2b.h"
+namespace {
/* Set up all data. */
-static void init(struct bs2b *bs2b)
+void init(Bs2b::bs2b *bs2b)
{
float Fc_lo, Fc_hi;
float G_lo, G_hi;
- float x, g;
switch(bs2b->level)
{
- case BS2B_LOW_CLEVEL: /* Low crossfeed level */
+ case Bs2b::LowCLevel: /* Low crossfeed level */
Fc_lo = 360.0f;
Fc_hi = 501.0f;
G_lo = 0.398107170553497f;
G_hi = 0.205671765275719f;
break;
- case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
+ case Bs2b::MiddleCLevel: /* Middle crossfeed level */
Fc_lo = 500.0f;
Fc_hi = 711.0f;
G_lo = 0.459726988530872f;
G_hi = 0.228208484414988f;
break;
- case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
+ case Bs2b::HighCLevel: /* High crossfeed level (virtual speakers are closer to itself) */
Fc_lo = 700.0f;
Fc_hi = 1021.0f;
G_lo = 0.530884444230988f;
G_hi = 0.250105790667544f;
break;
- case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
+ case Bs2b::LowECLevel: /* Low easy crossfeed level */
Fc_lo = 360.0f;
Fc_hi = 494.0f;
G_lo = 0.316227766016838f;
G_hi = 0.168236228897329f;
break;
- case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
+ case Bs2b::MiddleECLevel: /* Middle easy crossfeed level */
Fc_lo = 500.0f;
Fc_hi = 689.0f;
G_lo = 0.354813389233575f;
G_hi = 0.187169483835901f;
break;
- default: /* High easy crossfeed level */
- bs2b->level = BS2B_HIGH_ECLEVEL;
+ case Bs2b::HighECLevel: /* High easy crossfeed level */
+ default:
+ bs2b->level = Bs2b::HighECLevel;
Fc_lo = 700.0f;
Fc_hi = 975.0f;
G_lo = 0.398107170553497f;
G_hi = 0.205671765275719f;
break;
- } /* switch */
+ }
- g = 1.0f / (1.0f - G_hi + G_lo);
+ float g{1.0f / (1.0f - G_hi + G_lo)};
/* $fc = $Fc / $s;
* $d = 1 / 2 / pi / $fc;
* $x = exp(-1 / $d);
*/
- x = std::exp(-al::numbers::pi_v<float>*2.0f*Fc_lo/static_cast<float>(bs2b->srate));
+ float x{ std::exp(-al::numbers::pi_v<float>*2.0f*Fc_lo/static_cast<float>(bs2b->srate))};
bs2b->b1_lo = x;
bs2b->a0_lo = G_lo * (1.0f - x) * g;
@@ -99,85 +101,84 @@ static void init(struct bs2b *bs2b)
bs2b->b1_hi = x;
bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g;
bs2b->a1_hi = -x * g;
-} /* init */
+}
+} // namespace
/* Exported functions.
* See descriptions in "bs2b.h"
*/
+namespace Bs2b {
-void bs2b_set_params(struct bs2b *bs2b, int level, int srate)
-{
- if(srate <= 0) srate = 1;
-
- bs2b->level = level;
- bs2b->srate = srate;
- init(bs2b);
-} /* bs2b_set_params */
-
-int bs2b_get_level(struct bs2b *bs2b)
+void bs2b::set_params(int level_, int srate_)
{
- return bs2b->level;
-} /* bs2b_get_level */
+ if(srate_ < 1)
+ throw std::runtime_error{"BS2B srate < 1"};
-int bs2b_get_srate(struct bs2b *bs2b)
-{
- return bs2b->srate;
-} /* bs2b_get_srate */
+ level = level_;
+ srate = srate_;
+ init(this);
+}
-void bs2b_clear(struct bs2b *bs2b)
+void bs2b::clear()
{
- std::fill(std::begin(bs2b->history), std::end(bs2b->history), bs2b::t_last_sample{});
-} /* bs2b_clear */
+ history.fill(bs2b::t_last_sample{});
+}
-void bs2b_cross_feed(struct bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo)
+void bs2b::cross_feed(float *RESTRICT Left, float *RESTRICT Right, size_t SamplesToDo)
{
- const float a0_lo{bs2b->a0_lo};
- const float b1_lo{bs2b->b1_lo};
- const float a0_hi{bs2b->a0_hi};
- const float a1_hi{bs2b->a1_hi};
- const float b1_hi{bs2b->b1_hi};
- float lsamples[128][2];
- float rsamples[128][2];
+ const float a0lo{a0_lo};
+ const float b1lo{b1_lo};
+ const float a0hi{a0_hi};
+ const float a1hi{a1_hi};
+ const float b1hi{b1_hi};
+ std::array<std::array<float,2>,128> samples;
for(size_t base{0};base < SamplesToDo;)
{
- const size_t todo{std::min<size_t>(128, SamplesToDo-base)};
+ const size_t todo{std::min(samples.size(), SamplesToDo-base)};
/* Process left input */
- float z_lo{bs2b->history[0].lo};
- float z_hi{bs2b->history[0].hi};
+ float z_lo{history[0].lo};
+ float z_hi{history[0].hi};
for(size_t i{0};i < todo;i++)
{
- lsamples[i][0] = a0_lo*Left[i] + z_lo;
- z_lo = b1_lo*lsamples[i][0];
-
- lsamples[i][1] = a0_hi*Left[i] + z_hi;
- z_hi = a1_hi*Left[i] + b1_hi*lsamples[i][1];
+ const float x{Left[i]};
+ float y{a0hi*x + z_hi};
+ z_hi = a1hi*x + b1hi*y;
+ samples[i][0] = y;
+
+ y = a0lo*x + z_lo;
+ z_lo = b1lo*y;
+ samples[i][1] = y;
}
- bs2b->history[0].lo = z_lo;
- bs2b->history[0].hi = z_hi;
+ history[0].lo = z_lo;
+ history[0].hi = z_hi;
/* Process right input */
- z_lo = bs2b->history[1].lo;
- z_hi = bs2b->history[1].hi;
+ z_lo = history[1].lo;
+ z_hi = history[1].hi;
for(size_t i{0};i < todo;i++)
{
- rsamples[i][0] = a0_lo*Right[i] + z_lo;
- z_lo = b1_lo*rsamples[i][0];
-
- rsamples[i][1] = a0_hi*Right[i] + z_hi;
- z_hi = a1_hi*Right[i] + b1_hi*rsamples[i][1];
+ const float x{Right[i]};
+ float y{a0lo*x + z_lo};
+ z_lo = b1lo*y;
+ samples[i][0] += y;
+
+ y = a0hi*x + z_hi;
+ z_hi = a1hi*x + b1hi*y;
+ samples[i][1] += y;
}
- bs2b->history[1].lo = z_lo;
- bs2b->history[1].hi = z_hi;
+ history[1].lo = z_lo;
+ history[1].hi = z_hi;
- /* Crossfeed */
for(size_t i{0};i < todo;i++)
- *(Left++) = lsamples[i][1] + rsamples[i][0];
+ *(Left++) = samples[i][0];
for(size_t i{0};i < todo;i++)
- *(Right++) = rsamples[i][1] + lsamples[i][0];
+ *(Right++) = samples[i][1];
base += todo;
}
-} /* bs2b_cross_feed */
+}
+
+} // namespace Bs2b
diff --git a/core/bs2b.h b/core/bs2b.h
index 4d0b9dd8..6fb54c0c 100644
--- a/core/bs2b.h
+++ b/core/bs2b.h
@@ -24,66 +24,65 @@
#ifndef CORE_BS2B_H
#define CORE_BS2B_H
-#include "almalloc.h"
+#include <array>
-/* Number of crossfeed levels */
-#define BS2B_CLEVELS 3
+namespace Bs2b {
-/* Normal crossfeed levels */
-#define BS2B_HIGH_CLEVEL 3
-#define BS2B_MIDDLE_CLEVEL 2
-#define BS2B_LOW_CLEVEL 1
+enum {
+ /* Normal crossfeed levels */
+ LowCLevel = 1,
+ MiddleCLevel = 2,
+ HighCLevel = 3,
-/* Easy crossfeed levels */
-#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS
-#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS
-#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS
+ /* Easy crossfeed levels */
+ LowECLevel = 4,
+ MiddleECLevel = 5,
+ HighECLevel = 6,
-/* Default crossfeed levels */
-#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL
-/* Default sample rate (Hz) */
-#define BS2B_DEFAULT_SRATE 44100
+ DefaultCLevel = HighECLevel
+};
struct bs2b {
- int level; /* Crossfeed level */
- int srate; /* Sample rate (Hz) */
+ int level{}; /* Crossfeed level */
+ int srate{}; /* Sample rate (Hz) */
/* Lowpass IIR filter coefficients */
- float a0_lo;
- float b1_lo;
+ float a0_lo{};
+ float b1_lo{};
/* Highboost IIR filter coefficients */
- float a0_hi;
- float a1_hi;
- float b1_hi;
+ float a0_hi{};
+ float a1_hi{};
+ float b1_hi{};
/* Buffer of filter history
* [0] - first channel, [1] - second channel
*/
struct t_last_sample {
- float lo;
- float hi;
- } history[2];
-
- DEF_NEWDEL(bs2b)
-};
+ float lo{};
+ float hi{};
+ };
+ std::array<t_last_sample,2> history{};
+
+ /* Clear buffers and set new coefficients with new crossfeed level and
+ * sample rate values.
+ * level - crossfeed level of *Level enum values.
+ * srate - sample rate by Hz.
+ */
+ void set_params(int level, int srate);
-/* Clear buffers and set new coefficients with new crossfeed level and sample
- * rate values.
- * level - crossfeed level of *LEVEL values.
- * srate - sample rate by Hz.
- */
-void bs2b_set_params(bs2b *bs2b, int level, int srate);
+ /* Return current crossfeed level value */
+ [[nodiscard]] auto get_level() const noexcept -> int { return level; }
-/* Return current crossfeed level value */
-int bs2b_get_level(bs2b *bs2b);
+ /* Return current sample rate value */
+ [[nodiscard]] auto get_srate() const noexcept -> int { return srate; }
-/* Return current sample rate value */
-int bs2b_get_srate(bs2b *bs2b);
+ /* Clear buffer */
+ void clear();
-/* Clear buffer */
-void bs2b_clear(bs2b *bs2b);
+ void cross_feed(float *Left, float *Right, size_t SamplesToDo);
+};
-void bs2b_cross_feed(bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo);
+} // namespace Bs2b
#endif /* CORE_BS2B_H */
diff --git a/core/bsinc_tables.cpp b/core/bsinc_tables.cpp
index 41102e9a..6e3ee338 100644
--- a/core/bsinc_tables.cpp
+++ b/core/bsinc_tables.cpp
@@ -5,9 +5,9 @@
#include <array>
#include <cassert>
#include <cmath>
+#include <cstddef>
#include <limits>
#include <memory>
-#include <stddef.h>
#include <stdexcept>
#include "alnumbers.h"
@@ -123,15 +123,13 @@ struct BSincHeader {
double beta{};
double scaleBase{};
- uint a[BSincScaleCount]{};
+ std::array<uint,BSincScaleCount> a{};
uint total_size{};
constexpr BSincHeader(uint Rejection, uint Order) noexcept
+ : width{CalcKaiserWidth(Rejection, Order)}, beta{CalcKaiserBeta(Rejection)}
+ , scaleBase{width / 2.0}
{
- width = CalcKaiserWidth(Rejection, Order);
- beta = CalcKaiserBeta(Rejection);
- scaleBase = width / 2.0;
-
uint num_points{Order+1};
for(uint si{0};si < BSincScaleCount;++si)
{
@@ -162,8 +160,9 @@ struct BSincFilterArray {
constexpr uint BSincPointsMax{(hdr.a[0]*2 + 3) & ~3u};
static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small");
- using filter_type = double[BSincPhaseCount+1][BSincPointsMax];
- auto filter = std::make_unique<filter_type[]>(BSincScaleCount);
+ using filter_type = std::array<std::array<double,BSincPointsMax>,BSincPhaseCount+1>;
+ auto filterptr = std::make_unique<std::array<filter_type,BSincScaleCount>>();
+ const auto filter = filterptr->begin();
const double besseli_0_beta{cyl_bessel_i(0, hdr.beta)};
@@ -254,8 +253,8 @@ struct BSincFilterArray {
assert(idx == hdr.total_size);
}
- constexpr const BSincHeader &getHeader() const noexcept { return hdr; }
- constexpr const float *getTable() const noexcept { return &mTable.front(); }
+ [[nodiscard]] constexpr auto getHeader() const noexcept -> const BSincHeader& { return hdr; }
+ [[nodiscard]] constexpr auto getTable() const noexcept -> const float* { return mTable.data(); }
};
const BSincFilterArray<bsinc12_hdr> bsinc12_filter{};
diff --git a/core/bsinc_tables.h b/core/bsinc_tables.h
index aca4b274..6c33bd56 100644
--- a/core/bsinc_tables.h
+++ b/core/bsinc_tables.h
@@ -1,13 +1,15 @@
#ifndef CORE_BSINC_TABLES_H
#define CORE_BSINC_TABLES_H
+#include <array>
+
#include "bsinc_defs.h"
struct BSincTable {
float scaleBase, scaleRange;
- unsigned int m[BSincScaleCount];
- unsigned int filterOffset[BSincScaleCount];
+ std::array<unsigned int,BSincScaleCount> m;
+ std::array<unsigned int,BSincScaleCount> filterOffset;
const float *Tab;
};
diff --git a/core/buffer_storage.cpp b/core/buffer_storage.cpp
index 6ffab124..a343b946 100644
--- a/core/buffer_storage.cpp
+++ b/core/buffer_storage.cpp
@@ -3,7 +3,7 @@
#include "buffer_storage.h"
-#include <stdint.h>
+#include <cstdint>
const char *NameFromFormat(FmtType type) noexcept
diff --git a/core/buffer_storage.h b/core/buffer_storage.h
index 3b581b5e..dec774bf 100644
--- a/core/buffer_storage.h
+++ b/core/buffer_storage.h
@@ -98,19 +98,19 @@ struct BufferStorage {
AmbiScaling mAmbiScaling{AmbiScaling::FuMa};
uint mAmbiOrder{0u};
- inline uint bytesFromFmt() const noexcept { return BytesFromFmt(mType); }
- inline uint channelsFromFmt() const noexcept
+ [[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromFmt(mType); }
+ [[nodiscard]] auto channelsFromFmt() const noexcept -> uint
{ return ChannelsFromFmt(mChannels, mAmbiOrder); }
- inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); }
+ [[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint { return channelsFromFmt() * bytesFromFmt(); }
- inline uint blockSizeFromFmt() const noexcept
+ [[nodiscard]] auto blockSizeFromFmt() const noexcept -> uint
{
if(mType == FmtIMA4) return ((mBlockAlign-1)/2 + 4) * channelsFromFmt();
if(mType == FmtMSADPCM) return ((mBlockAlign-2)/2 + 7) * channelsFromFmt();
return frameSizeFromFmt();
};
- inline bool isBFormat() const noexcept { return IsBFormat(mChannels); }
+ [[nodiscard]] auto isBFormat() const noexcept -> bool { return IsBFormat(mChannels); }
};
#endif /* CORE_BUFFER_STORAGE_H */
diff --git a/core/context.cpp b/core/context.cpp
index 2ebbc7b1..0fe50d25 100644
--- a/core/context.cpp
+++ b/core/context.cpp
@@ -2,6 +2,7 @@
#include "config.h"
#include <cassert>
+#include <functional>
#include <limits>
#include <memory>
#include <stdexcept>
@@ -26,43 +27,14 @@ ContextBase::ContextBase(DeviceBase *device) : mDevice{device}
ContextBase::~ContextBase()
{
- size_t count{0};
- ContextProps *cprops{mParams.ContextUpdate.exchange(nullptr, std::memory_order_relaxed)};
- if(cprops)
- {
- ++count;
- delete cprops;
- }
- cprops = mFreeContextProps.exchange(nullptr, std::memory_order_acquire);
- while(cprops)
- {
- std::unique_ptr<ContextProps> old{cprops};
- cprops = old->next.load(std::memory_order_relaxed);
- ++count;
- }
- TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s");
-
- count = 0;
- EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)};
- while(eprops)
- {
- std::unique_ptr<EffectSlotProps> old{eprops};
- eprops = old->next.load(std::memory_order_relaxed);
- ++count;
- }
- TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
-
- if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)})
- {
+ if(std::unique_ptr<EffectSlotArray> curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)})
std::destroy_n(curarray->end(), curarray->size());
- delete curarray;
- }
- delete mVoices.exchange(nullptr, std::memory_order_relaxed);
+ mVoices.store(nullptr, std::memory_order_relaxed);
if(mAsyncEvents)
{
- count = 0;
+ size_t count{0};
auto evt_vec = mAsyncEvents->getReadVector();
if(evt_vec.first.len > 0)
{
@@ -85,85 +57,131 @@ ContextBase::~ContextBase()
void ContextBase::allocVoiceChanges()
{
- constexpr size_t clustersize{128};
+ static constexpr size_t clustersize{std::tuple_size_v<VoiceChangeCluster::element_type>};
+
+ VoiceChangeCluster clusterptr{std::make_unique<VoiceChangeCluster::element_type>()};
+ const auto cluster = al::span{*clusterptr};
- VoiceChangeCluster cluster{std::make_unique<VoiceChange[]>(clustersize)};
for(size_t i{1};i < clustersize;++i)
cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed);
cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed);
- mVoiceChangeClusters.emplace_back(std::move(cluster));
- mVoiceChangeTail = mVoiceChangeClusters.back().get();
+ mVoiceChangeClusters.emplace_back(std::move(clusterptr));
+ mVoiceChangeTail = mVoiceChangeClusters.back()->data();
}
void ContextBase::allocVoiceProps()
{
- constexpr size_t clustersize{32};
+ static constexpr size_t clustersize{std::tuple_size_v<VoicePropsCluster::element_type>};
TRACE("Increasing allocated voice properties to %zu\n",
(mVoicePropClusters.size()+1) * clustersize);
- VoicePropsCluster cluster{std::make_unique<VoicePropsItem[]>(clustersize)};
+ auto clusterptr = std::make_unique<VoicePropsCluster::element_type>();
+ auto cluster = al::span{*clusterptr};
for(size_t i{1};i < clustersize;++i)
cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed);
- mVoicePropClusters.emplace_back(std::move(cluster));
+ mVoicePropClusters.emplace_back(std::move(clusterptr));
VoicePropsItem *oldhead{mFreeVoiceProps.load(std::memory_order_acquire)};
do {
- mVoicePropClusters.back()[clustersize-1].next.store(oldhead, std::memory_order_relaxed);
- } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back().get(),
+ mVoicePropClusters.back()->back().next.store(oldhead, std::memory_order_relaxed);
+ } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back()->data(),
std::memory_order_acq_rel, std::memory_order_acquire) == false);
}
void ContextBase::allocVoices(size_t addcount)
{
- constexpr size_t clustersize{32};
+ static constexpr size_t clustersize{std::tuple_size_v<VoiceCluster::element_type>};
/* Convert element count to cluster count. */
addcount = (addcount+(clustersize-1)) / clustersize;
+ if(!addcount)
+ {
+ if(!mVoiceClusters.empty())
+ return;
+ ++addcount;
+ }
+
if(addcount >= std::numeric_limits<int>::max()/clustersize - mVoiceClusters.size())
throw std::runtime_error{"Allocating too many voices"};
const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize};
TRACE("Increasing allocated voices to %zu\n", totalcount);
- auto newarray = VoiceArray::Create(totalcount);
while(addcount)
{
- mVoiceClusters.emplace_back(std::make_unique<Voice[]>(clustersize));
+ mVoiceClusters.emplace_back(std::make_unique<VoiceCluster::element_type>());
--addcount;
}
+ auto newarray = VoiceArray::Create(totalcount);
auto voice_iter = newarray->begin();
for(VoiceCluster &cluster : mVoiceClusters)
- {
- for(size_t i{0};i < clustersize;++i)
- *(voice_iter++) = &cluster[i];
- }
+ voice_iter = std::transform(cluster->begin(), cluster->end(), voice_iter,
+ [](Voice &voice) noexcept -> Voice* { return &voice; });
- if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel))
- {
- mDevice->waitForMix();
- delete oldvoices;
- }
+ if(auto oldvoices = mVoices.exchange(std::move(newarray), std::memory_order_acq_rel))
+ std::ignore = mDevice->waitForMix();
}
+void ContextBase::allocEffectSlotProps()
+{
+ static constexpr size_t clustersize{std::tuple_size_v<EffectSlotPropsCluster::element_type>};
+
+ TRACE("Increasing allocated effect slot properties to %zu\n",
+ (mEffectSlotPropClusters.size()+1) * clustersize);
+
+ auto clusterptr = std::make_unique<EffectSlotPropsCluster::element_type>();
+ auto cluster = al::span{*clusterptr};
+ for(size_t i{1};i < clustersize;++i)
+ cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed);
+ auto *newcluster = mEffectSlotPropClusters.emplace_back(std::move(clusterptr)).get();
+
+ EffectSlotProps *oldhead{mFreeEffectSlotProps.load(std::memory_order_acquire)};
+ do {
+ newcluster->back().next.store(oldhead, std::memory_order_relaxed);
+ } while(mFreeEffectSlotProps.compare_exchange_weak(oldhead, newcluster->data(),
+ std::memory_order_acq_rel, std::memory_order_acquire) == false);
+}
+
EffectSlot *ContextBase::getEffectSlot()
{
- for(auto& cluster : mEffectSlotClusters)
+ for(auto& clusterptr : mEffectSlotClusters)
{
- for(size_t i{0};i < EffectSlotClusterSize;++i)
- {
- if(!cluster[i].InUse)
- return &cluster[i];
- }
+ const auto cluster = al::span{*clusterptr};
+ auto iter = std::find_if_not(cluster.begin(), cluster.end(),
+ std::mem_fn(&EffectSlot::InUse));
+ if(iter != cluster.end()) return al::to_address(iter);
}
- if(1 >= std::numeric_limits<int>::max()/EffectSlotClusterSize - mEffectSlotClusters.size())
+ auto clusterptr = std::make_unique<EffectSlotCluster::element_type>();
+ if(1 >= std::numeric_limits<int>::max()/clusterptr->size() - mEffectSlotClusters.size())
throw std::runtime_error{"Allocating too many effect slots"};
- const size_t totalcount{(mEffectSlotClusters.size()+1) * EffectSlotClusterSize};
+ const size_t totalcount{(mEffectSlotClusters.size()+1) * clusterptr->size()};
TRACE("Increasing allocated effect slots to %zu\n", totalcount);
- mEffectSlotClusters.emplace_back(std::make_unique<EffectSlot[]>(EffectSlotClusterSize));
- return getEffectSlot();
+ mEffectSlotClusters.emplace_back(std::move(clusterptr));
+ return mEffectSlotClusters.back()->data();
+}
+
+
+void ContextBase::allocContextProps()
+{
+ static constexpr size_t clustersize{std::tuple_size_v<ContextPropsCluster::element_type>};
+
+ TRACE("Increasing allocated context properties to %zu\n",
+ (mContextPropClusters.size()+1) * clustersize);
+
+ auto clusterptr = std::make_unique<ContextPropsCluster::element_type>();
+ auto cluster = al::span{*clusterptr};
+ for(size_t i{1};i < clustersize;++i)
+ cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed);
+ auto *newcluster = mContextPropClusters.emplace_back(std::move(clusterptr)).get();
+
+ ContextProps *oldhead{mFreeContextProps.load(std::memory_order_acquire)};
+ do {
+ newcluster->back().next.store(oldhead, std::memory_order_relaxed);
+ } while(mFreeContextProps.compare_exchange_weak(oldhead, newcluster->data(),
+ std::memory_order_acq_rel, std::memory_order_acquire) == false);
}
diff --git a/core/context.h b/core/context.h
index ccb7dd3b..980514b3 100644
--- a/core/context.h
+++ b/core/context.h
@@ -14,6 +14,7 @@
#include "alspan.h"
#include "async_event.h"
#include "atomic.h"
+#include "flexarray.h"
#include "opthelpers.h"
#include "vecmat.h"
@@ -26,9 +27,9 @@ struct VoiceChange;
struct VoicePropsItem;
-constexpr float SpeedOfSoundMetersPerSec{343.3f};
+inline constexpr float SpeedOfSoundMetersPerSec{343.3f};
-constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */
+inline constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */
enum class DistanceModel : unsigned char {
Disable,
@@ -56,8 +57,6 @@ struct ContextProps {
DistanceModel mDistanceModel;
std::atomic<ContextProps*> next;
-
- DEF_NEWDEL(ContextProps)
};
struct ContextParams {
@@ -85,7 +84,7 @@ struct ContextBase {
/* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
* indicates if updates are currently happening).
*/
- RefCount mUpdateCount{0u};
+ std::atomic<unsigned int> mUpdateCount{0u};
std::atomic<bool> mHoldUpdates{false};
std::atomic<bool> mStopVoicesOnDisconnect{true};
@@ -96,7 +95,7 @@ struct ContextBase {
*/
std::atomic<ContextProps*> mFreeContextProps{nullptr};
std::atomic<VoicePropsItem*> mFreeVoiceProps{nullptr};
- std::atomic<EffectSlotProps*> mFreeEffectslotProps{nullptr};
+ std::atomic<EffectSlotProps*> mFreeEffectSlotProps{nullptr};
/* The voice change tail is the beginning of the "free" elements, up to and
* *excluding* the current. If tail==current, there's no free elements and
@@ -108,21 +107,22 @@ struct ContextBase {
void allocVoiceChanges();
void allocVoiceProps();
-
+ void allocEffectSlotProps();
+ void allocContextProps();
ContextParams mParams;
using VoiceArray = al::FlexArray<Voice*>;
- std::atomic<VoiceArray*> mVoices{};
+ al::atomic_unique_ptr<VoiceArray> mVoices{};
std::atomic<size_t> mActiveVoiceCount{};
void allocVoices(size_t addcount);
- al::span<Voice*> getVoicesSpan() const noexcept
+ [[nodiscard]] auto getVoicesSpan() const noexcept -> al::span<Voice*>
{
return {mVoices.load(std::memory_order_relaxed)->data(),
mActiveVoiceCount.load(std::memory_order_relaxed)};
}
- al::span<Voice*> getVoicesSpanAcquired() const noexcept
+ [[nodiscard]] auto getVoicesSpanAcquired() const noexcept -> al::span<Voice*>
{
return {mVoices.load(std::memory_order_acquire)->data(),
mActiveVoiceCount.load(std::memory_order_acquire)};
@@ -143,22 +143,30 @@ struct ContextBase {
* However, to avoid allocating each object individually, they're allocated
* in clusters that are stored in a vector for easy automatic cleanup.
*/
- using VoiceChangeCluster = std::unique_ptr<VoiceChange[]>;
+ using VoiceChangeCluster = std::unique_ptr<std::array<VoiceChange,128>>;
std::vector<VoiceChangeCluster> mVoiceChangeClusters;
- using VoiceCluster = std::unique_ptr<Voice[]>;
+ using VoiceCluster = std::unique_ptr<std::array<Voice,32>>;
std::vector<VoiceCluster> mVoiceClusters;
- using VoicePropsCluster = std::unique_ptr<VoicePropsItem[]>;
+ using VoicePropsCluster = std::unique_ptr<std::array<VoicePropsItem,32>>;
std::vector<VoicePropsCluster> mVoicePropClusters;
- static constexpr size_t EffectSlotClusterSize{4};
EffectSlot *getEffectSlot();
- using EffectSlotCluster = std::unique_ptr<EffectSlot[]>;
+ using EffectSlotCluster = std::unique_ptr<std::array<EffectSlot,4>>;
std::vector<EffectSlotCluster> mEffectSlotClusters;
+ using EffectSlotPropsCluster = std::unique_ptr<std::array<EffectSlotProps,4>>;
+ std::vector<EffectSlotPropsCluster> mEffectSlotPropClusters;
+
+ /* This could be greater than 2, but there should be no way there can be
+ * more than two context property updates in use simultaneously.
+ */
+ using ContextPropsCluster = std::unique_ptr<std::array<ContextProps,2>>;
+ std::vector<ContextPropsCluster> mContextPropClusters;
+
ContextBase(DeviceBase *device);
ContextBase(const ContextBase&) = delete;
diff --git a/core/converter.cpp b/core/converter.cpp
index 5b2f3e15..805b8548 100644
--- a/core/converter.cpp
+++ b/core/converter.cpp
@@ -9,7 +9,7 @@
#include <cstddef>
#include <cstdint>
#include <iterator>
-#include <limits.h>
+#include <climits>
#include "albit.h"
#include "alnumeric.h"
@@ -24,26 +24,23 @@ static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for Buffer
static_assert((INT_MAX>>MixerFracBits)/MaxPitch > BufferLineSize,
"MaxPitch and/or BufferLineSize are too large for MixerFracBits!");
-/* Base template left undefined. Should be marked =delete, but Clang 3.8.1
- * chokes on that given the inline specializations.
- */
template<DevFmtType T>
-inline float LoadSample(DevFmtType_t<T> val) noexcept;
+constexpr float LoadSample(DevFmtType_t<T> val) noexcept = delete;
-template<> inline float LoadSample<DevFmtByte>(DevFmtType_t<DevFmtByte> val) noexcept
-{ return val * (1.0f/128.0f); }
-template<> inline float LoadSample<DevFmtShort>(DevFmtType_t<DevFmtShort> val) noexcept
-{ return val * (1.0f/32768.0f); }
-template<> inline float LoadSample<DevFmtInt>(DevFmtType_t<DevFmtInt> val) noexcept
+template<> constexpr float LoadSample<DevFmtByte>(DevFmtType_t<DevFmtByte> val) noexcept
+{ return float(val) * (1.0f/128.0f); }
+template<> constexpr float LoadSample<DevFmtShort>(DevFmtType_t<DevFmtShort> val) noexcept
+{ return float(val) * (1.0f/32768.0f); }
+template<> constexpr float LoadSample<DevFmtInt>(DevFmtType_t<DevFmtInt> val) noexcept
{ return static_cast<float>(val) * (1.0f/2147483648.0f); }
-template<> inline float LoadSample<DevFmtFloat>(DevFmtType_t<DevFmtFloat> val) noexcept
+template<> constexpr float LoadSample<DevFmtFloat>(DevFmtType_t<DevFmtFloat> val) noexcept
{ return val; }
-template<> inline float LoadSample<DevFmtUByte>(DevFmtType_t<DevFmtUByte> val) noexcept
+template<> constexpr float LoadSample<DevFmtUByte>(DevFmtType_t<DevFmtUByte> val) noexcept
{ return LoadSample<DevFmtByte>(static_cast<int8_t>(val - 128)); }
-template<> inline float LoadSample<DevFmtUShort>(DevFmtType_t<DevFmtUShort> val) noexcept
+template<> constexpr float LoadSample<DevFmtUShort>(DevFmtType_t<DevFmtUShort> val) noexcept
{ return LoadSample<DevFmtShort>(static_cast<int16_t>(val - 32768)); }
-template<> inline float LoadSample<DevFmtUInt>(DevFmtType_t<DevFmtUInt> val) noexcept
+template<> constexpr float LoadSample<DevFmtUInt>(DevFmtType_t<DevFmtUInt> val) noexcept
{ return LoadSample<DevFmtInt>(static_cast<int32_t>(val - 2147483648u)); }
@@ -51,7 +48,7 @@ template<DevFmtType T>
inline void LoadSampleArray(float *RESTRICT dst, const void *src, const size_t srcstep,
const size_t samples) noexcept
{
- const DevFmtType_t<T> *ssrc = static_cast<const DevFmtType_t<T>*>(src);
+ auto *ssrc = static_cast<const DevFmtType_t<T>*>(src);
for(size_t i{0u};i < samples;i++)
dst[i] = LoadSample<T>(ssrc[i*srcstep]);
}
@@ -99,7 +96,7 @@ template<DevFmtType T>
inline void StoreSampleArray(void *dst, const float *RESTRICT src, const size_t dststep,
const size_t samples) noexcept
{
- DevFmtType_t<T> *sdst = static_cast<DevFmtType_t<T>*>(dst);
+ auto *sdst = static_cast<DevFmtType_t<T>*>(dst);
for(size_t i{0u};i < samples;i++)
sdst[i*dststep] = StoreSample<T>(src[i]);
}
@@ -127,7 +124,7 @@ void StoreSamples(void *dst, const float *src, const size_t dststep, const DevFm
template<DevFmtType T>
void Mono2Stereo(float *RESTRICT dst, const void *src, const size_t frames) noexcept
{
- const DevFmtType_t<T> *ssrc = static_cast<const DevFmtType_t<T>*>(src);
+ auto *ssrc = static_cast<const DevFmtType_t<T>*>(src);
for(size_t i{0u};i < frames;i++)
dst[i*2 + 1] = dst[i*2 + 0] = LoadSample<T>(ssrc[i]) * 0.707106781187f;
}
@@ -136,7 +133,7 @@ template<DevFmtType T>
void Multi2Mono(uint chanmask, const size_t step, const float scale, float *RESTRICT dst,
const void *src, const size_t frames) noexcept
{
- const DevFmtType_t<T> *ssrc = static_cast<const DevFmtType_t<T>*>(src);
+ auto *ssrc = static_cast<const DevFmtType_t<T>*>(src);
std::fill_n(dst, frames, 0.0f);
for(size_t c{0};chanmask;++c)
{
@@ -216,8 +213,8 @@ uint SampleConverter::availableOut(uint srcframes) const
uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint dstframes)
{
- const uint SrcFrameSize{static_cast<uint>(mChan.size()) * mSrcTypeSize};
- const uint DstFrameSize{static_cast<uint>(mChan.size()) * mDstTypeSize};
+ const size_t SrcFrameSize{mChan.size() * mSrcTypeSize};
+ const size_t DstFrameSize{mChan.size() * mDstTypeSize};
const uint increment{mIncrement};
auto SamplesIn = static_cast<const std::byte*>(*src);
uint NumSrcSamples{*srcframes};
@@ -243,8 +240,8 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
break;
}
- float *RESTRICT SrcData{mSrcSamples};
- float *RESTRICT DstData{mDstSamples};
+ float *RESTRICT SrcData{mSrcSamples.data()};
+ float *RESTRICT DstData{mDstSamples.data()};
uint DataPosFrac{mFracOffset};
uint64_t DataSize64{prepcount};
DataSize64 += readable;
@@ -271,13 +268,13 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
/* 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);
+ std::copy_n(mChan[chan].PrevSamples.cbegin(), prepcount, SrcData);
LoadSamples(SrcData + prepcount, SrcSamples, mChan.size(), 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::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples.begin());
std::fill(std::begin(mChan[chan].PrevSamples)+nextprep,
std::end(mChan[chan].PrevSamples), 0.0f);
@@ -328,9 +325,9 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con
*/
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;
+ auto *samples = static_cast<const std::byte*>(src[chan]);
+ LoadSamples(&mChan[chan].PrevSamples[prepcount], samples, 1, mSrcType, readable);
+ src[chan] = samples + size_t{mSrcTypeSize}*readable;
}
mSrcPrepCount = prepcount + readable;
@@ -338,8 +335,8 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con
break;
}
- float *RESTRICT SrcData{mSrcSamples};
- float *RESTRICT DstData{mDstSamples};
+ float *RESTRICT SrcData{mSrcSamples.data()};
+ float *RESTRICT DstData{mDstSamples.data()};
uint DataPosFrac{mFracOffset};
uint64_t DataSize64{prepcount};
DataSize64 += readable;
@@ -363,13 +360,13 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con
/* 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);
+ std::copy_n(mChan[chan].PrevSamples.cbegin(), 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::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples.begin());
std::fill(std::begin(mChan[chan].PrevSamples)+nextprep,
std::end(mChan[chan].PrevSamples), 0.0f);
@@ -377,7 +374,7 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con
mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment,
{DstData, DstSize});
- std::byte *DstSamples = static_cast<std::byte*>(dst[chan]) + pos*mDstTypeSize;
+ auto *DstSamples = static_cast<std::byte*>(dst[chan]) + pos*size_t{mDstTypeSize};
StoreSamples(DstSamples, DstData, 1, mDstType, DstSize);
}
@@ -390,7 +387,7 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *con
/* 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;
+ src[chan] = static_cast<const std::byte*>(src[chan]) + size_t{mSrcTypeSize}*srcread;
NumSrcSamples -= srcread;
pos += DstSize;
diff --git a/core/converter.h b/core/converter.h
index 49ca124d..3dc2babb 100644
--- a/core/converter.h
+++ b/core/converter.h
@@ -7,6 +7,7 @@
#include "almalloc.h"
#include "devformat.h"
+#include "flexarray.h"
#include "mixer/defs.h"
using uint = unsigned int;
@@ -25,22 +26,22 @@ struct SampleConverter {
InterpState mState{};
ResamplerFunc mResample{};
- alignas(16) float mSrcSamples[BufferLineSize]{};
- alignas(16) float mDstSamples[BufferLineSize]{};
+ alignas(16) FloatBufferLine mSrcSamples{};
+ alignas(16) FloatBufferLine mDstSamples{};
struct ChanSamples {
- alignas(16) float PrevSamples[MaxResamplerPadding];
+ alignas(16) std::array<float,MaxResamplerPadding> PrevSamples;
};
al::FlexArray<ChanSamples> mChan;
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;
+ [[nodiscard]] auto convert(const void **src, uint *srcframes, void *dst, uint dstframes) -> uint;
+ [[nodiscard]] auto convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes) -> uint;
+ [[nodiscard]] auto availableOut(uint srcframes) const -> uint;
using SampleOffset = std::chrono::duration<int64_t, std::ratio<1,MixerFracOne>>;
- SampleOffset currentInputDelay() const noexcept
+ [[nodiscard]] auto currentInputDelay() const noexcept -> SampleOffset
{
const int64_t prep{int64_t{mSrcPrepCount} - MaxResamplerEdge};
return SampleOffset{(prep<<MixerFracBits) + mFracOffset};
@@ -59,7 +60,7 @@ struct ChannelConverter {
uint mChanMask{};
DevFmtChannels mDstChans{};
- bool is_active() const noexcept { return mChanMask != 0; }
+ [[nodiscard]] auto is_active() const noexcept -> bool { return mChanMask != 0; }
void convert(const void *src, float *dst, uint frames) const;
};
diff --git a/core/cubic_defs.h b/core/cubic_defs.h
index 33751c97..f3ded415 100644
--- a/core/cubic_defs.h
+++ b/core/cubic_defs.h
@@ -1,13 +1,15 @@
#ifndef CORE_CUBIC_DEFS_H
#define CORE_CUBIC_DEFS_H
+#include <array>
+
/* The number of distinct phase intervals within the cubic filter tables. */
constexpr unsigned int CubicPhaseBits{5};
constexpr unsigned int CubicPhaseCount{1 << CubicPhaseBits};
struct CubicCoefficients {
- float mCoeffs[4];
- float mDeltas[4];
+ std::array<float,4> mCoeffs;
+ std::array<float,4> mDeltas;
};
#endif /* CORE_CUBIC_DEFS_H */
diff --git a/core/cubic_tables.cpp b/core/cubic_tables.cpp
index 5e7aafad..84462893 100644
--- a/core/cubic_tables.cpp
+++ b/core/cubic_tables.cpp
@@ -2,7 +2,7 @@
#include "cubic_tables.h"
#include <array>
-#include <stddef.h>
+#include <cstddef>
#include "cubic_defs.h"
@@ -41,7 +41,7 @@ struct SplineFilterArray {
mTable[pi].mDeltas[3] = -mTable[pi].mCoeffs[3];
}
- constexpr auto& getTable() const noexcept { return mTable; }
+ [[nodiscard]] constexpr auto& getTable() const noexcept { return mTable; }
};
constexpr SplineFilterArray SplineFilter{};
diff --git a/core/dbus_wrap.cpp b/core/dbus_wrap.cpp
index 48419566..05d9fc06 100644
--- a/core/dbus_wrap.cpp
+++ b/core/dbus_wrap.cpp
@@ -8,16 +8,22 @@
#include <mutex>
#include <type_traits>
-#include "albit.h"
#include "logging.h"
void PrepareDBus()
{
- static constexpr char libname[] = "libdbus-1.so.3";
+ const char *libname{"libdbus-1.so.3"};
+
+ dbus_handle = LoadLib(libname);
+ if(!dbus_handle)
+ {
+ WARN("Failed to load %s\n", libname);
+ return;
+ }
auto load_func = [](auto &f, const char *name) -> void
- { f = al::bit_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); };
+ { f = reinterpret_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); };
#define LOAD_FUNC(x) do { \
load_func(p##x, #x); \
if(!p##x) \
@@ -29,14 +35,8 @@ void PrepareDBus()
} \
} while(0);
- dbus_handle = LoadLib(libname);
- if(!dbus_handle)
- {
- WARN("Failed to load %s\n", libname);
- return;
- }
+ DBUS_FUNCTIONS(LOAD_FUNC)
-DBUS_FUNCTIONS(LOAD_FUNC)
#undef LOAD_FUNC
}
#endif
diff --git a/core/devformat.h b/core/devformat.h
index 485826a3..d918e531 100644
--- a/core/devformat.h
+++ b/core/devformat.h
@@ -2,6 +2,7 @@
#define CORE_DEVFORMAT_H
#include <cstdint>
+#include <cstddef>
using uint = unsigned int;
@@ -71,7 +72,7 @@ enum DevFmtChannels : unsigned char {
DevFmtChannelsDefault = DevFmtStereo
};
-#define MAX_OUTPUT_CHANNELS 16
+inline constexpr size_t MaxOutputChannels{16};
/* DevFmtType traits, providing the type, etc given a DevFmtType. */
template<DevFmtType T>
diff --git a/core/device.cpp b/core/device.cpp
index 2766c5e4..795a9601 100644
--- a/core/device.cpp
+++ b/core/device.cpp
@@ -9,15 +9,12 @@
#include "mastering.h"
-al::FlexArray<ContextBase*> DeviceBase::sEmptyContextArray{0u};
+static_assert(std::atomic<std::chrono::nanoseconds>::is_always_lock_free);
-DeviceBase::DeviceBase(DeviceType type) : Type{type}, mContexts{&sEmptyContextArray}
+DeviceBase::DeviceBase(DeviceType type)
+ : Type{type}, mContexts{al::FlexArray<ContextBase*>::Create(0)}
{
}
-DeviceBase::~DeviceBase()
-{
- auto *oldarray = mContexts.exchange(nullptr, std::memory_order_relaxed);
- if(oldarray != &sEmptyContextArray) delete oldarray;
-}
+DeviceBase::~DeviceBase() = default;
diff --git a/core/device.h b/core/device.h
index b1ffc9ce..1da08727 100644
--- a/core/device.h
+++ b/core/device.h
@@ -17,6 +17,7 @@
#include "bufferline.h"
#include "devformat.h"
#include "filters/nfc.h"
+#include "flexarray.h"
#include "intrusive_ptr.h"
#include "mixer/hrtfdefs.h"
#include "opthelpers.h"
@@ -25,8 +26,10 @@
#include "vector.h"
class BFormatDec;
+namespace Bs2b {
struct bs2b;
-struct Compressor;
+} // namespace Bs2b
+class Compressor;
struct ContextBase;
struct DirectHrtfState;
struct HrtfStore;
@@ -34,12 +37,12 @@ struct HrtfStore;
using uint = unsigned int;
-#define MIN_OUTPUT_RATE 8000
-#define MAX_OUTPUT_RATE 192000
-#define DEFAULT_OUTPUT_RATE 48000
+inline constexpr size_t MinOutputRate{8000};
+inline constexpr size_t MaxOutputRate{192000};
+inline constexpr size_t DefaultOutputRate{48000};
-#define DEFAULT_UPDATE_SIZE 960 /* 20ms */
-#define DEFAULT_NUM_UPDATES 3
+inline constexpr size_t DefaultUpdateSize{960}; /* 20ms */
+inline constexpr size_t DefaultNumUpdates{3};
enum class DeviceType : uint8_t {
@@ -82,7 +85,7 @@ struct DistanceComp {
float *Buffer{nullptr};
};
- std::array<ChanData,MAX_OUTPUT_CHANNELS> mChannels;
+ std::array<ChanData,MaxOutputChannels> mChannels;
al::FlexArray<float,16> mSamples;
DistanceComp(size_t count) : mSamples{count} { }
@@ -158,8 +161,6 @@ enum {
// Specifies if the DSP is paused at user request
DevicePaused,
- // Specifies if the device is currently running
- DeviceRunning,
// Specifies if the output plays directly on/in ears (headphones, headset,
// ear buds, etc).
@@ -173,12 +174,13 @@ enum {
DeviceFlagsCount
};
-struct DeviceBase {
- /* To avoid extraneous allocations, a 0-sized FlexArray<ContextBase*> is
- * defined globally as a sharable object.
- */
- static al::FlexArray<ContextBase*> sEmptyContextArray;
+enum class DeviceState : uint8_t {
+ Unprepared,
+ Configured,
+ Playing
+};
+struct DeviceBase {
std::atomic<bool> Connected{true};
const DeviceType Type{};
@@ -202,6 +204,7 @@ struct DeviceBase {
// Device flags
std::bitset<DeviceFlagsCount> Flags{};
+ DeviceState mDeviceState{DeviceState::Unprepared};
uint NumAuxSends{};
@@ -218,8 +221,8 @@ struct DeviceBase {
*/
NfcFilter mNFCtrlFilter{};
- uint SamplesDone{0u};
- std::chrono::nanoseconds ClockBase{0};
+ std::atomic<uint> mSamplesDone{0u};
+ std::atomic<std::chrono::nanoseconds> mClockBase{std::chrono::nanoseconds{}};
std::chrono::nanoseconds FixedLatency{0};
AmbiRotateMatrix mAmbiRotateMatrix{};
@@ -229,24 +232,24 @@ struct DeviceBase {
static constexpr size_t MixerLineSize{BufferLineSize + DecoderBase::sMaxPadding};
static constexpr size_t MixerChannelsMax{16};
using MixerBufferLine = std::array<float,MixerLineSize>;
- alignas(16) std::array<MixerBufferLine,MixerChannelsMax> mSampleData;
- alignas(16) std::array<float,MixerLineSize+MaxResamplerPadding> mResampleData;
+ alignas(16) std::array<MixerBufferLine,MixerChannelsMax> mSampleData{};
+ alignas(16) std::array<float,MixerLineSize+MaxResamplerPadding> mResampleData{};
- alignas(16) float FilteredData[BufferLineSize];
+ alignas(16) std::array<float,BufferLineSize> FilteredData{};
union {
- alignas(16) float HrtfSourceData[BufferLineSize + HrtfHistoryLength];
- alignas(16) float NfcSampleData[BufferLineSize];
+ alignas(16) std::array<float,BufferLineSize+HrtfHistoryLength> HrtfSourceData{};
+ alignas(16) std::array<float,BufferLineSize> NfcSampleData;
};
/* Persistent storage for HRTF mixing. */
- alignas(16) float2 HrtfAccumData[BufferLineSize + HrirLength];
+ alignas(16) std::array<float2,BufferLineSize+HrirLength> HrtfAccumData{};
/* Mixing buffer used by the Dry mix and Real output. */
al::vector<FloatBufferLine, 16> MixBuffer;
/* The "dry" path corresponds to the main output. */
MixParams Dry;
- uint NumChannelsPerOrder[MaxAmbiOrder+1]{};
+ std::array<uint,MaxAmbiOrder+1> NumChannelsPerOrder{};
/* "Real" output, which will be written to the device buffer. May alias the
* dry buffer.
@@ -265,7 +268,7 @@ struct DeviceBase {
std::unique_ptr<BFormatDec> AmbiDecoder;
/* Stereo-to-binaural filter */
- std::unique_ptr<bs2b> Bs2b;
+ std::unique_ptr<Bs2b::bs2b> Bs2b;
using PostProc = void(DeviceBase::*)(const size_t SamplesToDo);
PostProc PostProcess{nullptr};
@@ -284,10 +287,10 @@ struct DeviceBase {
* the end, so the bottom bit indicates if the device is currently mixing
* and the upper bits indicates how many mixes have been done.
*/
- RefCount MixCount{0u};
+ std::atomic<uint> mMixCount{0u};
// Contexts created on this device
- std::atomic<al::FlexArray<ContextBase*>*> mContexts{nullptr};
+ al::atomic_unique_ptr<al::FlexArray<ContextBase*>> mContexts;
DeviceBase(DeviceType type);
@@ -295,18 +298,56 @@ struct DeviceBase {
DeviceBase& operator=(const DeviceBase&) = delete;
~DeviceBase();
- uint bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); }
- uint channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); }
- uint frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); }
+ [[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromDevFmt(FmtType); }
+ [[nodiscard]] auto channelsFromFmt() const noexcept -> uint { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); }
+ [[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint { return bytesFromFmt() * channelsFromFmt(); }
+
+ struct MixLock {
+ std::atomic<uint> &mCount;
+ const uint mLastVal;
+
+ MixLock(std::atomic<uint> &count, const uint last_val) noexcept
+ : mCount{count}, mLastVal{last_val}
+ { }
+ /* Increment the mix count when the lock goes out of scope to "release"
+ * it (lsb should be 0).
+ */
+ ~MixLock() { mCount.store(mLastVal+2, std::memory_order_release); }
+ };
+ auto getWriteMixLock() noexcept
+ {
+ /* Increment the mix count at the start of mixing and writing clock
+ * info (lsb should be 1).
+ */
+ const auto mixCount = mMixCount.load(std::memory_order_relaxed);
+ mMixCount.store(mixCount+1, std::memory_order_relaxed);
+ std::atomic_thread_fence(std::memory_order_release);
+ return MixLock{mMixCount, mixCount};
+ }
- uint waitForMix() const noexcept
+ /** Waits for the mixer to not be mixing or updating the clock. */
+ [[nodiscard]] auto waitForMix() const noexcept -> uint
{
uint refcount;
- while((refcount=MixCount.load(std::memory_order_acquire))&1) {
+ while((refcount=mMixCount.load(std::memory_order_acquire))&1) {
}
return refcount;
}
+ /**
+ * Helper to get the current clock time from the device's ClockBase, and
+ * SamplesDone converted from the sample rate. Should only be called while
+ * watching the MixCount.
+ */
+ [[nodiscard]] auto getClockTime() const noexcept -> std::chrono::nanoseconds
+ {
+ using std::chrono::seconds;
+ using std::chrono::nanoseconds;
+
+ auto ns = nanoseconds{seconds{mSamplesDone.load(std::memory_order_relaxed)}} / Frequency;
+ return mClockBase.load(std::memory_order_relaxed) + ns;
+ }
+
void ProcessHrtf(const size_t SamplesToDo);
void ProcessAmbiDec(const size_t SamplesToDo);
void ProcessAmbiDecStablized(const size_t SamplesToDo);
@@ -320,8 +361,8 @@ struct DeviceBase {
void renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep);
/* Caller must lock the device state, and the mixer must not be running. */
-#ifdef __USE_MINGW_ANSI_STDIO
- [[gnu::format(gnu_printf,2,3)]]
+#ifdef __MINGW32__
+ [[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]]
#else
[[gnu::format(printf,2,3)]]
#endif
@@ -331,11 +372,9 @@ struct DeviceBase {
* Returns the index for the given channel name (e.g. FrontCenter), or
* InvalidChannelIndex if it doesn't exist.
*/
- uint8_t channelIdxByName(Channel chan) const noexcept
+ [[nodiscard]] auto channelIdxByName(Channel chan) const noexcept -> uint8_t
{ return RealOut.ChannelIndex[chan]; }
- DISABLE_ALLOC()
-
private:
uint renderSamples(const uint numSamples);
};
diff --git a/core/effects/base.h b/core/effects/base.h
index 83df7cf0..df4eb129 100644
--- a/core/effects/base.h
+++ b/core/effects/base.h
@@ -2,7 +2,8 @@
#define CORE_EFFECTS_BASE_H
#include <array>
-#include <stddef.h>
+#include <cstddef>
+#include <variant>
#include "almalloc.h"
#include "alspan.h"
@@ -19,21 +20,21 @@ struct RealMixParams;
/** Target gain for the reverb decay feedback reaching the decay time. */
-constexpr float ReverbDecayGain{0.001f}; /* -60 dB */
+inline constexpr float ReverbDecayGain{0.001f}; /* -60 dB */
-constexpr float ReverbMaxReflectionsDelay{0.3f};
-constexpr float ReverbMaxLateReverbDelay{0.1f};
+inline constexpr float ReverbMaxReflectionsDelay{0.3f};
+inline constexpr float ReverbMaxLateReverbDelay{0.1f};
enum class ChorusWaveform {
Sinusoid,
Triangle
};
-constexpr float ChorusMaxDelay{0.016f};
-constexpr float FlangerMaxDelay{0.004f};
+inline constexpr float ChorusMaxDelay{0.016f};
+inline constexpr float FlangerMaxDelay{0.004f};
-constexpr float EchoMaxDelay{0.207f};
-constexpr float EchoMaxLRDelay{0.404f};
+inline constexpr float EchoMaxDelay{0.207f};
+inline constexpr float EchoMaxLRDelay{0.404f};
enum class FShifterDirection {
Down,
@@ -59,120 +60,148 @@ enum class VMorpherWaveform {
Sawtooth
};
-union EffectProps {
- struct {
- float Density;
- float Diffusion;
- float Gain;
- float GainHF;
- float GainLF;
- float DecayTime;
- float DecayHFRatio;
- float DecayLFRatio;
- float ReflectionsGain;
- float ReflectionsDelay;
- float ReflectionsPan[3];
- float LateReverbGain;
- float LateReverbDelay;
- float LateReverbPan[3];
- float EchoTime;
- float EchoDepth;
- float ModulationTime;
- float ModulationDepth;
- float AirAbsorptionGainHF;
- float HFReference;
- float LFReference;
- float RoomRolloffFactor;
- bool DecayHFLimit;
- } Reverb;
-
- struct {
- float AttackTime;
- float ReleaseTime;
- float Resonance;
- float PeakGain;
- } Autowah;
-
- struct {
- ChorusWaveform Waveform;
- int Phase;
- float Rate;
- float Depth;
- float Feedback;
- float Delay;
- } Chorus; /* Also Flanger */
-
- struct {
- bool OnOff;
- } Compressor;
-
- struct {
- float Edge;
- float Gain;
- float LowpassCutoff;
- float EQCenter;
- float EQBandwidth;
- } Distortion;
-
- struct {
- float Delay;
- float LRDelay;
-
- float Damping;
- float Feedback;
-
- float Spread;
- } Echo;
-
- struct {
- float LowCutoff;
- float LowGain;
- float Mid1Center;
- float Mid1Gain;
- float Mid1Width;
- float Mid2Center;
- float Mid2Gain;
- float Mid2Width;
- float HighCutoff;
- float HighGain;
- } Equalizer;
-
- struct {
- float Frequency;
- FShifterDirection LeftDirection;
- FShifterDirection RightDirection;
- } Fshifter;
-
- struct {
- float Frequency;
- float HighPassCutoff;
- ModulatorWaveform Waveform;
- } Modulator;
-
- struct {
- int CoarseTune;
- int FineTune;
- } Pshifter;
-
- struct {
- float Rate;
- VMorpherPhenome PhonemeA;
- VMorpherPhenome PhonemeB;
- int PhonemeACoarseTuning;
- int PhonemeBCoarseTuning;
- VMorpherWaveform Waveform;
- } Vmorpher;
-
- struct {
- float Gain;
- } Dedicated;
-
- struct {
- std::array<float,3> OrientAt;
- std::array<float,3> OrientUp;
- } Convolution;
+struct ReverbProps {
+ float Density;
+ float Diffusion;
+ float Gain;
+ float GainHF;
+ float GainLF;
+ float DecayTime;
+ float DecayHFRatio;
+ float DecayLFRatio;
+ float ReflectionsGain;
+ float ReflectionsDelay;
+ std::array<float,3> ReflectionsPan;
+ float LateReverbGain;
+ float LateReverbDelay;
+ std::array<float,3> LateReverbPan;
+ float EchoTime;
+ float EchoDepth;
+ float ModulationTime;
+ float ModulationDepth;
+ float AirAbsorptionGainHF;
+ float HFReference;
+ float LFReference;
+ float RoomRolloffFactor;
+ bool DecayHFLimit;
};
+struct AutowahProps {
+ float AttackTime;
+ float ReleaseTime;
+ float Resonance;
+ float PeakGain;
+};
+
+struct ChorusProps {
+ ChorusWaveform Waveform;
+ int Phase;
+ float Rate;
+ float Depth;
+ float Feedback;
+ float Delay;
+};
+
+struct FlangerProps {
+ ChorusWaveform Waveform;
+ int Phase;
+ float Rate;
+ float Depth;
+ float Feedback;
+ float Delay;
+};
+
+struct CompressorProps {
+ bool OnOff;
+};
+
+struct DistortionProps {
+ float Edge;
+ float Gain;
+ float LowpassCutoff;
+ float EQCenter;
+ float EQBandwidth;
+};
+
+struct EchoProps {
+ float Delay;
+ float LRDelay;
+
+ float Damping;
+ float Feedback;
+
+ float Spread;
+};
+
+struct EqualizerProps {
+ float LowCutoff;
+ float LowGain;
+ float Mid1Center;
+ float Mid1Gain;
+ float Mid1Width;
+ float Mid2Center;
+ float Mid2Gain;
+ float Mid2Width;
+ float HighCutoff;
+ float HighGain;
+};
+
+struct FshifterProps {
+ float Frequency;
+ FShifterDirection LeftDirection;
+ FShifterDirection RightDirection;
+};
+
+struct ModulatorProps {
+ float Frequency;
+ float HighPassCutoff;
+ ModulatorWaveform Waveform;
+};
+
+struct PshifterProps {
+ int CoarseTune;
+ int FineTune;
+};
+
+struct VmorpherProps {
+ float Rate;
+ VMorpherPhenome PhonemeA;
+ VMorpherPhenome PhonemeB;
+ int PhonemeACoarseTuning;
+ int PhonemeBCoarseTuning;
+ VMorpherWaveform Waveform;
+};
+
+struct DedicatedDialogProps {
+ float Gain;
+};
+
+struct DedicatedLfeProps {
+ float Gain;
+};
+
+struct ConvolutionProps {
+ std::array<float,3> OrientAt;
+ std::array<float,3> OrientUp;
+};
+
+using EffectProps = std::variant<std::monostate,
+ ReverbProps,
+ AutowahProps,
+ ChorusProps,
+ FlangerProps,
+ CompressorProps,
+ DistortionProps,
+ EchoProps,
+ EqualizerProps,
+ FshifterProps,
+ ModulatorProps,
+ PshifterProps,
+ VmorpherProps,
+ DedicatedDialogProps,
+ DedicatedLfeProps,
+ ConvolutionProps>;
+
struct EffectTarget {
MixParams *Main;
diff --git a/core/effectslot.cpp b/core/effectslot.cpp
index db8aa078..6c80317e 100644
--- a/core/effectslot.cpp
+++ b/core/effectslot.cpp
@@ -3,7 +3,7 @@
#include "effectslot.h"
-#include <stddef.h>
+#include <cstddef>
#include "almalloc.h"
#include "context.h"
@@ -14,6 +14,8 @@ EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept
/* Allocate space for twice as many pointers, so the mixer has scratch
* space to store a sorted list during mixing.
*/
- void *ptr{al_calloc(alignof(EffectSlotArray), EffectSlotArray::Sizeof(count*2))};
- return al::construct_at(static_cast<EffectSlotArray*>(ptr), count);
+ static constexpr auto AlignVal = std::align_val_t{alignof(EffectSlotArray)};
+ if(gsl::owner<void*> ptr{::operator new[](EffectSlotArray::Sizeof(count*2), AlignVal)})
+ return al::construct_at(static_cast<EffectSlotArray*>(ptr), count);
+ return nullptr;
}
diff --git a/core/effectslot.h b/core/effectslot.h
index 2624ae5f..cf8503ff 100644
--- a/core/effectslot.h
+++ b/core/effectslot.h
@@ -6,6 +6,7 @@
#include "almalloc.h"
#include "device.h"
#include "effects/base.h"
+#include "flexarray.h"
#include "intrusive_ptr.h"
struct EffectSlot;
@@ -45,8 +46,6 @@ struct EffectSlotProps {
al::intrusive_ptr<EffectState> State;
std::atomic<EffectSlotProps*> next;
-
- DEF_NEWDEL(EffectSlotProps)
};
@@ -82,8 +81,6 @@ struct EffectSlot {
static EffectSlotArray *CreatePtrArray(size_t count) noexcept;
-
- DEF_NEWDEL(EffectSlot)
};
#endif /* CORE_EFFECTSLOT_H */
diff --git a/core/except.cpp b/core/except.cpp
index 45fd4eb5..86c7ccca 100644
--- a/core/except.cpp
+++ b/core/except.cpp
@@ -21,7 +21,7 @@ void base_exception::setMessage(const char* msg, std::va_list args)
if(msglen > 0) LIKELY
{
mMessage.resize(static_cast<size_t>(msglen)+1);
- std::vsnprintf(const_cast<char*>(mMessage.data()), mMessage.length(), msg, args2);
+ std::vsnprintf(mMessage.data(), mMessage.length(), msg, args2);
mMessage.pop_back();
}
va_end(args2);
diff --git a/core/except.h b/core/except.h
index 0e28e9df..90e3346e 100644
--- a/core/except.h
+++ b/core/except.h
@@ -14,12 +14,13 @@ class base_exception : public std::exception {
protected:
base_exception() = default;
- virtual ~base_exception();
- void setMessage(const char *msg, std::va_list args);
+ auto setMessage(const char *msg, std::va_list args) -> void;
public:
- const char *what() const noexcept override { return mMessage.c_str(); }
+ ~base_exception() override;
+
+ [[nodiscard]] auto what() const noexcept -> const char* override { return mMessage.c_str(); }
};
} // namespace al
diff --git a/core/filters/biquad.cpp b/core/filters/biquad.cpp
index a0a62eb8..6671f60f 100644
--- a/core/filters/biquad.cpp
+++ b/core/filters/biquad.cpp
@@ -27,8 +27,8 @@ void BiquadFilterR<Real>::setParams(BiquadType type, Real f0norm, Real gain, Rea
const Real alpha{sin_w0/2.0f * rcpQ};
Real sqrtgain_alpha_2;
- Real a[3]{ 1.0f, 0.0f, 0.0f };
- Real b[3]{ 1.0f, 0.0f, 0.0f };
+ std::array<Real,3> a{{1.0f, 0.0f, 0.0f}};
+ std::array<Real,3> b{{1.0f, 0.0f, 0.0f}};
/* Calculate filter coefficients depending on filter type */
switch(type)
diff --git a/core/filters/biquad.h b/core/filters/biquad.h
index 75a4009b..e176caae 100644
--- a/core/filters/biquad.h
+++ b/core/filters/biquad.h
@@ -119,9 +119,9 @@ public:
void dualProcess(BiquadFilterR &other, const al::span<const Real> src, Real *dst);
/* Rather hacky. It's just here to support "manual" processing. */
- std::pair<Real,Real> getComponents() const noexcept { return {mZ1, mZ2}; }
+ [[nodiscard]] auto getComponents() const noexcept -> std::pair<Real,Real> { return {mZ1, mZ2}; }
void setComponents(Real z1, Real z2) noexcept { mZ1 = z1; mZ2 = z2; }
- Real processOne(const Real in, Real &z1, Real &z2) const noexcept
+ [[nodiscard]] auto processOne(const Real in, Real &z1, Real &z2) const noexcept -> Real
{
const Real out{in*mB0 + z1};
z1 = in*mB1 - out*mA1 + z2;
diff --git a/core/filters/nfc.cpp b/core/filters/nfc.cpp
index aa64c613..95b84e2c 100644
--- a/core/filters/nfc.cpp
+++ b/core/filters/nfc.cpp
@@ -48,12 +48,12 @@
namespace {
-constexpr float B[5][4] = {
- { 0.0f },
- { 1.0f },
- { 3.0f, 3.0f },
- { 3.6778f, 6.4595f, 2.3222f },
- { 4.2076f, 11.4877f, 5.7924f, 9.1401f }
+constexpr std::array B{
+ std::array{ 0.0f, 0.0f, 0.0f, 0.0f},
+ std::array{ 1.0f, 0.0f, 0.0f, 0.0f},
+ std::array{ 3.0f, 3.0f, 0.0f, 0.0f},
+ std::array{3.6778f, 6.4595f, 2.3222f, 0.0f},
+ std::array{4.2076f, 11.4877f, 5.7924f, 9.1401f}
};
NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept
diff --git a/core/filters/nfc.h b/core/filters/nfc.h
index 4b8e68b5..9c58f863 100644
--- a/core/filters/nfc.h
+++ b/core/filters/nfc.h
@@ -1,30 +1,31 @@
#ifndef CORE_FILTERS_NFC_H
#define CORE_FILTERS_NFC_H
+#include <array>
#include <cstddef>
#include "alspan.h"
struct NfcFilter1 {
- float base_gain, gain;
- float b1, a1;
- float z[1];
+ float base_gain{1.0f}, gain{1.0f};
+ float b1{}, a1{};
+ std::array<float,1> z{};
};
struct NfcFilter2 {
- float base_gain, gain;
- float b1, b2, a1, a2;
- float z[2];
+ float base_gain{1.0f}, gain{1.0f};
+ float b1{}, b2{}, a1{}, a2{};
+ std::array<float,2> z{};
};
struct NfcFilter3 {
- float base_gain, gain;
- float b1, b2, b3, a1, a2, a3;
- float z[3];
+ float base_gain{1.0f}, gain{1.0f};
+ float b1{}, b2{}, b3{}, a1{}, a2{}, a3{};
+ std::array<float,3> z{};
};
struct NfcFilter4 {
- float base_gain, gain;
- float b1, b2, b3, b4, a1, a2, a3, a4;
- float z[4];
+ float base_gain{1.0f}, gain{1.0f};
+ float b1{}, b2{}, b3{}, b4{}, a1{}, a2{}, a3{}, a4{};
+ std::array<float,4> z{};
};
class NfcFilter {
diff --git a/core/fmt_traits.cpp b/core/fmt_traits.cpp
index 054d8766..9d79287d 100644
--- a/core/fmt_traits.cpp
+++ b/core/fmt_traits.cpp
@@ -6,7 +6,7 @@
namespace al {
-const int16_t muLawDecompressionTable[256] = {
+const std::array<int16_t,256> muLawDecompressionTable{{
-32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
-23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
-15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
@@ -39,9 +39,9 @@ const int16_t muLawDecompressionTable[256] = {
244, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64,
56, 48, 40, 32, 24, 16, 8, 0
-};
+}};
-const int16_t aLawDecompressionTable[256] = {
+const std::array<int16_t,256> aLawDecompressionTable{{
-5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
-7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
-2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
@@ -74,6 +74,6 @@ const int16_t aLawDecompressionTable[256] = {
1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
688, 656, 752, 720, 560, 528, 624, 592,
944, 912, 1008, 976, 816, 784, 880, 848
-};
+}};
} // namespace al
diff --git a/core/fmt_traits.h b/core/fmt_traits.h
index 02473014..101e20b6 100644
--- a/core/fmt_traits.h
+++ b/core/fmt_traits.h
@@ -1,6 +1,7 @@
#ifndef CORE_FMT_TRAITS_H
#define CORE_FMT_TRAITS_H
+#include <array>
#include <cstddef>
#include <stdint.h>
@@ -9,8 +10,8 @@
namespace al {
-extern const int16_t muLawDecompressionTable[256];
-extern const int16_t aLawDecompressionTable[256];
+extern const std::array<int16_t,256> muLawDecompressionTable;
+extern const std::array<int16_t,256> aLawDecompressionTable;
template<FmtType T>
diff --git a/core/fpu_ctrl.cpp b/core/fpu_ctrl.cpp
index 435855ad..28e60c04 100644
--- a/core/fpu_ctrl.cpp
+++ b/core/fpu_ctrl.cpp
@@ -64,29 +64,24 @@ void reset_fpu(unsigned int state [[maybe_unused]])
} // namespace
-void FPUCtl::enter() noexcept
+unsigned int FPUCtl::Set() noexcept
{
- if(this->in_mode) return;
-
+ unsigned int state{};
#if defined(HAVE_SSE_INTRINSICS)
- disable_denormals(&this->sse_state);
+ disable_denormals(&state);
#elif defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
- disable_denormals(&this->sse_state);
+ disable_denormals(&state);
#endif
-
- this->in_mode = true;
+ return state;
}
-void FPUCtl::leave() noexcept
+void FPUCtl::Reset(unsigned int state [[maybe_unused]]) noexcept
{
- if(!this->in_mode) return;
-
#if defined(HAVE_SSE_INTRINSICS)
- reset_fpu(this->sse_state);
+ reset_fpu(state);
#elif defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
- reset_fpu(this->sse_state);
+ reset_fpu(state);
#endif
- this->in_mode = false;
}
diff --git a/core/fpu_ctrl.h b/core/fpu_ctrl.h
index 9554313a..d4f75ec3 100644
--- a/core/fpu_ctrl.h
+++ b/core/fpu_ctrl.h
@@ -2,20 +2,31 @@
#define CORE_FPU_CTRL_H
class FPUCtl {
-#if defined(HAVE_SSE_INTRINSICS) || (defined(__GNUC__) && defined(HAVE_SSE))
unsigned int sse_state{};
-#endif
bool in_mode{};
+ static unsigned int Set() noexcept;
+ static void Reset(unsigned int state) noexcept;
+
public:
- FPUCtl() noexcept { enter(); in_mode = true; }
- ~FPUCtl() { if(in_mode) leave(); }
+ FPUCtl() noexcept : sse_state{Set()}, in_mode{true} { }
+ ~FPUCtl() { if(in_mode) Reset(sse_state); }
FPUCtl(const FPUCtl&) = delete;
FPUCtl& operator=(const FPUCtl&) = delete;
- void enter() noexcept;
- void leave() noexcept;
+ void enter() noexcept
+ {
+ if(!in_mode)
+ sse_state = Set();
+ in_mode = true;
+ }
+ void leave() noexcept
+ {
+ if(in_mode)
+ Reset(sse_state);
+ in_mode = false;
+ }
};
#endif /* CORE_FPU_CTRL_H */
diff --git a/core/front_stablizer.h b/core/front_stablizer.h
index 6825111a..8eeb6d74 100644
--- a/core/front_stablizer.h
+++ b/core/front_stablizer.h
@@ -7,6 +7,7 @@
#include "almalloc.h"
#include "bufferline.h"
#include "filters/splitter.h"
+#include "flexarray.h"
struct FrontStablizer {
diff --git a/core/helpers.cpp b/core/helpers.cpp
index 5a996eee..b669b8d6 100644
--- a/core/helpers.cpp
+++ b/core/helpers.cpp
@@ -177,7 +177,7 @@ std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
return results;
}
-void SetRTPriority(void)
+void SetRTPriority()
{
#if !defined(ALSOFT_UWP)
if(RTPrioLevel > 0)
@@ -256,7 +256,7 @@ const PathNamePair &GetProcBinary()
#ifndef __SWITCH__
if(pathname.empty())
{
- const char *SelfLinkNames[]{
+ std::array SelfLinkNames{
"/proc/self/exe",
"/proc/self/file",
"/proc/curproc/exe",
diff --git a/core/helpers.h b/core/helpers.h
index df51c116..64fc67fb 100644
--- a/core/helpers.h
+++ b/core/helpers.h
@@ -15,11 +15,11 @@ struct PathNamePair {
: path{std::forward<T>(path_)}, fname{std::forward<U>(fname_)}
{ }
};
-const PathNamePair &GetProcBinary(void);
+const PathNamePair &GetProcBinary();
extern int RTPrioLevel;
extern bool AllowRTTimeLimit;
-void SetRTPriority(void);
+void SetRTPriority();
std::vector<std::string> SearchDataFiles(const char *match, const char *subdir);
diff --git a/core/hrtf.cpp b/core/hrtf.cpp
index 9a13a004..eef68bf9 100644
--- a/core/hrtf.cpp
+++ b/core/hrtf.cpp
@@ -18,6 +18,7 @@
#include <mutex>
#include <numeric>
#include <optional>
+#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
@@ -88,10 +89,12 @@ constexpr uint HrirDelayFracHalf{HrirDelayFracOne >> 1};
static_assert(MaxHrirDelay*HrirDelayFracOne < 256, "MAX_HRIR_DELAY or DELAY_FRAC too large");
+/* NOLINTBEGIN(*-avoid-c-arrays) */
constexpr char magicMarker00[8]{'M','i','n','P','H','R','0','0'};
constexpr char magicMarker01[8]{'M','i','n','P','H','R','0','1'};
constexpr char magicMarker02[8]{'M','i','n','P','H','R','0','2'};
constexpr char magicMarker03[8]{'M','i','n','P','H','R','0','3'};
+/* NOLINTEND(*-avoid-c-arrays) */
/* First value for pass-through coefficients (remaining are 0), used for omni-
* directional sounds. */
@@ -231,29 +234,29 @@ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float
const auto az1 = CalcAzIndex(mElev[ebase + elev1_idx].azCount, azimuth);
/* Calculate the HRIR indices to blend. */
- const size_t idx[4]{
+ const std::array<size_t,4> idx{{
ir0offset + az0.idx,
ir0offset + ((az0.idx+1) % mElev[ebase + elev0.idx].azCount),
ir1offset + az1.idx,
ir1offset + ((az1.idx+1) % mElev[ebase + elev1_idx].azCount)
- };
+ }};
/* Calculate bilinear blending weights, attenuated according to the
* directional panning factor.
*/
- const float blend[4]{
+ const std::array<float,4> blend{{
(1.0f-elev0.blend) * (1.0f-az0.blend) * dirfact,
(1.0f-elev0.blend) * ( az0.blend) * dirfact,
( elev0.blend) * (1.0f-az1.blend) * dirfact,
( elev0.blend) * ( az1.blend) * dirfact
- };
+ }};
/* Calculate the blended HRIR delays. */
- float d{mDelays[idx[0]][0]*blend[0] + mDelays[idx[1]][0]*blend[1] + mDelays[idx[2]][0]*blend[2]
- + mDelays[idx[3]][0]*blend[3]};
+ float d{float(mDelays[idx[0]][0])*blend[0] + float(mDelays[idx[1]][0])*blend[1]
+ + float(mDelays[idx[2]][0])*blend[2] + float(mDelays[idx[3]][0])*blend[3]};
delays[0] = fastf2u(d * float{1.0f/HrirDelayFracOne});
- d = mDelays[idx[0]][1]*blend[0] + mDelays[idx[1]][1]*blend[1] + mDelays[idx[2]][1]*blend[2]
- + mDelays[idx[3]][1]*blend[3];
+ d = float(mDelays[idx[0]][1])*blend[0] + float(mDelays[idx[1]][1])*blend[1]
+ + float(mDelays[idx[2]][1])*blend[2] + float(mDelays[idx[3]][1])*blend[3];
delays[1] = fastf2u(d * float{1.0f/HrirDelayFracOne});
/* Calculate the blended HRIR coefficients. */
@@ -267,7 +270,7 @@ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float
const float mult{blend[c]};
auto blend_coeffs = [mult](const float src, const float coeff) noexcept -> float
{ return src*mult + coeff; };
- std::transform(srccoeffs, srccoeffs + HrirLength*2, coeffout, coeffout, blend_coeffs);
+ std::transform(srccoeffs, srccoeffs + HrirLength*2_uz, coeffout, coeffout, blend_coeffs);
}
}
@@ -276,7 +279,8 @@ std::unique_ptr<DirectHrtfState> DirectHrtfState::Create(size_t num_chans)
{ return std::unique_ptr<DirectHrtfState>{new(FamCount(num_chans)) DirectHrtfState{num_chans}}; }
void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin,
- const al::span<const AngularPoint> AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels],
+ const al::span<const AngularPoint> AmbiPoints,
+ const al::span<const std::array<float,MaxAmbiChannels>> AmbiMatrix,
const float XOverFreq, const al::span<const float,MaxAmbiOrder+1> AmbiOrderHFGain)
{
using double2 = std::array<double,2>;
@@ -287,7 +291,8 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool
const double xover_norm{double{XOverFreq} / Hrtf->mSampleRate};
mChannels[0].mSplitter.init(static_cast<float>(xover_norm));
- for(size_t i{0};i < mChannels.size();++i)
+ mChannels[0].mHfScale = AmbiOrderHFGain[0];
+ for(size_t i{1};i < mChannels.size();++i)
{
const size_t order{AmbiIndex::OrderFromChannel[i]};
mChannels[i].mSplitter = mChannels[0].mSplitter;
@@ -307,7 +312,7 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool
const auto az0 = CalcAzIndex(Hrtf->mElev[elev0.idx].azCount, pt.Azim.value);
const auto az1 = CalcAzIndex(Hrtf->mElev[elev1_idx].azCount, pt.Azim.value);
- const size_t idx[4]{
+ const std::array<size_t,4> idx{
ir0offset + az0.idx,
ir0offset + ((az0.idx+1) % Hrtf->mElev[elev0.idx].azCount),
ir1offset + az1.idx,
@@ -333,34 +338,38 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool
auto tmpres = std::vector<std::array<double2,HrirLength>>(mChannels.size());
max_delay = 0;
- for(size_t c{0u};c < AmbiPoints.size();++c)
+ auto matrixline = AmbiMatrix.cbegin();
+ for(auto &impulse : impres)
{
- const ConstHrirSpan hrir{impres[c].hrir};
- const uint base_delay{perHrirMin ? minu(impres[c].ldelay, impres[c].rdelay) : min_delay};
- const uint ldelay{hrir_delay_round(impres[c].ldelay - base_delay)};
- const uint rdelay{hrir_delay_round(impres[c].rdelay - base_delay)};
- max_delay = maxu(max_delay, maxu(impres[c].ldelay, impres[c].rdelay) - base_delay);
-
- for(size_t i{0u};i < mChannels.size();++i)
+ const ConstHrirSpan hrir{impulse.hrir};
+ const uint base_delay{perHrirMin ? std::min(impulse.ldelay, impulse.rdelay) : min_delay};
+ const uint ldelay{hrir_delay_round(impulse.ldelay - base_delay)};
+ const uint rdelay{hrir_delay_round(impulse.rdelay - base_delay)};
+ max_delay = std::max(max_delay, std::max(impulse.ldelay, impulse.rdelay) - base_delay);
+
+ auto gains = matrixline->cbegin();
+ ++matrixline;
+ for(auto &result : tmpres)
{
- const double mult{AmbiMatrix[c][i]};
- const size_t numirs{HrirLength - maxz(ldelay, rdelay)};
+ const double mult{*(gains++)};
+ const size_t numirs{HrirLength - std::max(ldelay, rdelay)};
size_t lidx{ldelay}, ridx{rdelay};
for(size_t j{0};j < numirs;++j)
{
- tmpres[i][lidx++][0] += hrir[j][0] * mult;
- tmpres[i][ridx++][1] += hrir[j][1] * mult;
+ result[lidx++][0] += hrir[j][0] * mult;
+ result[ridx++][1] += hrir[j][1] * mult;
}
}
}
impres.clear();
- for(size_t i{0u};i < mChannels.size();++i)
+ auto output = mChannels.begin();
+ for(auto &result : tmpres)
{
- auto copy_arr = [](const double2 &in) noexcept -> float2
+ auto cast_array2 = [](const double2 &in) noexcept -> float2
{ return float2{{static_cast<float>(in[0]), static_cast<float>(in[1])}}; };
- std::transform(tmpres[i].cbegin(), tmpres[i].cend(), mChannels[i].mCoeffs.begin(),
- copy_arr);
+ std::transform(result.cbegin(), result.cend(), output->mCoeffs.begin(), cast_array2);
+ ++output;
}
tmpres.clear();
@@ -378,6 +387,10 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize,
const al::span<const HrtfStore::Elevation> elevs, const HrirArray *coeffs,
const ubyte2 *delays, const char *filename)
{
+ static_assert(alignof(HrtfStore::Field) <= alignof(HrtfStore));
+ static_assert(alignof(HrtfStore::Elevation) <= alignof(HrtfStore));
+ static_assert(16 <= alignof(HrtfStore));
+
const size_t irCount{size_t{elevs.back().azCount} + elevs.back().irOffset};
size_t total{sizeof(HrtfStore)};
total = RoundUp(total, alignof(HrtfStore::Field)); /* Align for field infos */
@@ -388,11 +401,12 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize,
total += sizeof(std::declval<HrtfStore&>().mCoeffs[0])*irCount;
total += sizeof(std::declval<HrtfStore&>().mDelays[0])*irCount;
+ static constexpr auto AlignVal = std::align_val_t{alignof(HrtfStore)};
std::unique_ptr<HrtfStore> Hrtf{};
- if(void *ptr{al_calloc(16, total)})
+ if(gsl::owner<void*> ptr{::operator new[](total, AlignVal, std::nothrow)})
{
- Hrtf.reset(al::construct_at(static_cast<HrtfStore*>(ptr)));
- InitRef(Hrtf->mRef, 1u);
+ Hrtf = decltype(Hrtf){::new(ptr) HrtfStore{}};
+ Hrtf->mRef.store(1u, std::memory_order_relaxed);
Hrtf->mSampleRate = rate & 0xff'ff'ff;
Hrtf->mIrSize = irSize;
@@ -492,10 +506,10 @@ T> readle(std::istream &data)
static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type");
T ret{};
- std::byte b[sizeof(T)]{};
- if(!data.read(reinterpret_cast<char*>(b), num_bits/8))
+ std::array<std::byte,sizeof(T)> b{};
+ if(!data.read(reinterpret_cast<char*>(b.data()), num_bits/8))
return static_cast<T>(EOF);
- std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast<std::byte*>(&ret));
+ std::reverse_copy(b.begin(), b.end(), reinterpret_cast<std::byte*>(&ret));
return fixsign<num_bits>(ret);
}
@@ -576,7 +590,7 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename)
for(auto &hrir : coeffs)
{
for(auto &val : al::span<float2>{hrir.data(), irSize})
- val[0] = readle<int16_t>(data) / 32768.0f;
+ val[0] = float(readle<int16_t>(data)) / 32768.0f;
}
for(auto &val : delays)
val[0] = readle<uint8_t>(data);
@@ -598,9 +612,9 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename)
/* Mirror the left ear responses to the right ear. */
MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
- const HrtfStore::Field field[1]{{0.0f, evCount}};
- return CreateHrtfStore(rate, static_cast<uint8_t>(irSize), field, {elevs.data(), elevs.size()},
- coeffs.data(), delays.data(), filename);
+ const std::array field{HrtfStore::Field{0.0f, evCount}};
+ return CreateHrtfStore(rate, static_cast<uint8_t>(irSize), field, elevs, coeffs.data(),
+ delays.data(), filename);
}
std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename)
@@ -654,7 +668,7 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename)
for(auto &hrir : coeffs)
{
for(auto &val : al::span<float2>{hrir.data(), irSize})
- val[0] = readle<int16_t>(data) / 32768.0f;
+ val[0] = float(readle<int16_t>(data)) / 32768.0f;
}
for(auto &val : delays)
val[0] = readle<uint8_t>(data);
@@ -676,9 +690,8 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename)
/* Mirror the left ear responses to the right ear. */
MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
- const HrtfStore::Field field[1]{{0.0f, evCount}};
- return CreateHrtfStore(rate, irSize, field, {elevs.data(), elevs.size()}, coeffs.data(),
- delays.data(), filename);
+ const std::array field{HrtfStore::Field{0.0f, evCount}};
+ return CreateHrtfStore(rate, irSize, field, elevs, coeffs.data(), delays.data(), filename);
}
std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
@@ -747,7 +760,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
return nullptr;
}
- fields[f].distance = distance / 1000.0f;
+ fields[f].distance = float(distance) / 1000.0f;
fields[f].evCount = evCount;
if(f > 0 && fields[f].distance <= fields[f-1].distance)
{
@@ -796,7 +809,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
for(auto &hrir : coeffs)
{
for(auto &val : al::span<float2>{hrir.data(), irSize})
- val[0] = readle<int16_t>(data) / 32768.0f;
+ val[0] = float(readle<int16_t>(data)) / 32768.0f;
}
}
else if(sampleType == SampleType_S24)
@@ -835,8 +848,8 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
{
for(auto &val : al::span<float2>{hrir.data(), irSize})
{
- val[0] = readle<int16_t>(data) / 32768.0f;
- val[1] = readle<int16_t>(data) / 32768.0f;
+ val[0] = float(readle<int16_t>(data)) / 32768.0f;
+ val[1] = float(readle<int16_t>(data)) / 32768.0f;
}
}
}
@@ -901,7 +914,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
elevs__end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs__end);
return ebase + field.evCount;
};
- (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs);
+ std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs);
assert(elevs_.begin() == elevs__end);
/* Reestablish the IR offset for each elevation index, given the new
@@ -936,7 +949,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
return ebase + field.evCount;
};
- (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs);
+ std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs);
assert(coeffs_.begin() == coeffs_end);
assert(delays_.begin() == delays_end);
@@ -946,8 +959,7 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
delays = std::move(delays_);
}
- return CreateHrtfStore(rate, irSize, {fields.data(), fields.size()},
- {elevs.data(), elevs.size()}, coeffs.data(), delays.data(), filename);
+ return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data(), filename);
}
std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename)
@@ -1008,7 +1020,7 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename)
return nullptr;
}
- fields[f].distance = distance / 1000.0f;
+ fields[f].distance = float(distance) / 1000.0f;
fields[f].evCount = evCount;
if(f > 0 && fields[f].distance > fields[f-1].distance)
{
@@ -1115,8 +1127,7 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename)
}
}
- return CreateHrtfStore(rate, irSize, {fields.data(), fields.size()},
- {elevs.data(), elevs.size()}, coeffs.data(), delays.data(), filename);
+ return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data(), filename);
}
@@ -1206,6 +1217,7 @@ al::span<const char> GetResource(int /*name*/)
#else
+/* NOLINTNEXTLINE(*-avoid-c-arrays) */
constexpr unsigned char hrtf_default[]{
#include "default_hrtf.txt"
};
@@ -1329,32 +1341,32 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
}
std::unique_ptr<HrtfStore> hrtf;
- char magic[sizeof(magicMarker03)];
- stream->read(magic, sizeof(magic));
+ std::array<char,sizeof(magicMarker03)> magic{};
+ stream->read(magic.data(), magic.size());
if(stream->gcount() < static_cast<std::streamsize>(sizeof(magicMarker03)))
ERR("%s data is too short (%zu bytes)\n", name.c_str(), stream->gcount());
- else if(memcmp(magic, magicMarker03, sizeof(magicMarker03)) == 0)
+ else if(memcmp(magic.data(), magicMarker03, sizeof(magicMarker03)) == 0)
{
TRACE("Detected data set format v3\n");
hrtf = LoadHrtf03(*stream, name.c_str());
}
- else if(memcmp(magic, magicMarker02, sizeof(magicMarker02)) == 0)
+ else if(memcmp(magic.data(), magicMarker02, sizeof(magicMarker02)) == 0)
{
TRACE("Detected data set format v2\n");
hrtf = LoadHrtf02(*stream, name.c_str());
}
- else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0)
+ else if(memcmp(magic.data(), magicMarker01, sizeof(magicMarker01)) == 0)
{
TRACE("Detected data set format v1\n");
hrtf = LoadHrtf01(*stream, name.c_str());
}
- else if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0)
+ else if(memcmp(magic.data(), magicMarker00, sizeof(magicMarker00)) == 0)
{
TRACE("Detected data set format v0\n");
hrtf = LoadHrtf00(*stream, name.c_str());
}
else
- ERR("Invalid header in %s: \"%.8s\"\n", name.c_str(), magic);
+ ERR("Invalid header in %s: \"%.8s\"\n", name.c_str(), magic.data());
stream.reset();
if(!hrtf)
@@ -1380,7 +1392,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
rs.init(hrtf->mSampleRate, devrate);
for(size_t i{0};i < irCount;++i)
{
- HrirArray &coeffs = const_cast<HrirArray&>(hrtf->mCoeffs[i]);
+ auto &coeffs = const_cast<HrirArray&>(hrtf->mCoeffs[i]);
for(size_t j{0};j < 2;++j)
{
std::transform(coeffs.cbegin(), coeffs.cend(), inout[0].begin(),
@@ -1400,7 +1412,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
{
for(size_t j{0};j < 2;++j)
{
- const float new_delay{std::round(hrtf->mDelays[i][j] * rate_scale) /
+ const float new_delay{std::round(float(hrtf->mDelays[i][j]) * rate_scale) /
float{HrirDelayFracOne}};
max_delay = maxf(max_delay, new_delay);
new_delays[i][j] = new_delay;
@@ -1420,7 +1432,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
for(size_t i{0};i < irCount;++i)
{
- ubyte2 &delays = const_cast<ubyte2&>(hrtf->mDelays[i]);
+ auto &delays = const_cast<ubyte2&>(hrtf->mDelays[i]);
for(size_t j{0};j < 2;++j)
delays[j] = static_cast<ubyte>(float2int(new_delays[i][j]*delay_scale + 0.5f));
}
@@ -1459,9 +1471,9 @@ void HrtfStore::dec_ref()
auto remove_unused = [](LoadedHrtf &hrtf) -> bool
{
HrtfStore *entry{hrtf.mEntry.get()};
- if(entry && ReadRef(entry->mRef) == 0)
+ if(entry && entry->mRef.load() == 0)
{
- TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.data());
+ TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.c_str());
hrtf.mEntry = nullptr;
return true;
}
diff --git a/core/hrtf.h b/core/hrtf.h
index 5e6e09a8..882724b8 100644
--- a/core/hrtf.h
+++ b/core/hrtf.h
@@ -13,12 +13,13 @@
#include "atomic.h"
#include "ambidefs.h"
#include "bufferline.h"
-#include "mixer/hrtfdefs.h"
+#include "flexarray.h"
#include "intrusive_ptr.h"
+#include "mixer/hrtfdefs.h"
-struct HrtfStore {
- RefCount mRef;
+struct alignas(16) HrtfStore {
+ std::atomic<uint> mRef;
uint mSampleRate : 24;
uint mIrSize : 8;
@@ -46,7 +47,14 @@ struct HrtfStore {
void add_ref();
void dec_ref();
- DEF_PLACE_NEWDEL()
+ void *operator new(size_t) = delete;
+ void *operator new[](size_t) = delete;
+ void operator delete[](void*) noexcept = delete;
+
+ void operator delete(gsl::owner<void*> block, void*) noexcept
+ { ::operator delete[](block, std::align_val_t{alignof(HrtfStore)}); }
+ void operator delete(gsl::owner<void*> block) noexcept
+ { ::operator delete[](block, std::align_val_t{alignof(HrtfStore)}); }
};
using HrtfStorePtr = al::intrusive_ptr<HrtfStore>;
@@ -60,7 +68,7 @@ struct AngularPoint {
struct DirectHrtfState {
- std::array<float,BufferLineSize> mTemp;
+ std::array<float,BufferLineSize> mTemp{};
/* HRTF filter state for dry buffer content */
uint mIrSize{0};
@@ -74,7 +82,8 @@ struct DirectHrtfState {
* are ordered and scaled according to the matrix input.
*/
void build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin,
- const al::span<const AngularPoint> AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels],
+ const al::span<const AngularPoint> AmbiPoints,
+ const al::span<const std::array<float,MaxAmbiChannels>> AmbiMatrix,
const float XOverFreq, const al::span<const float,MaxAmbiOrder+1> AmbiOrderHFGain);
static std::unique_ptr<DirectHrtfState> Create(size_t num_chans);
diff --git a/core/logging.h b/core/logging.h
index 06b7cdde..a8d9b731 100644
--- a/core/logging.h
+++ b/core/logging.h
@@ -22,8 +22,8 @@ using LogCallbackFunc = void(*)(void *userptr, char level, const char *message,
void al_set_log_callback(LogCallbackFunc callback, void *userptr);
-#ifdef __USE_MINGW_ANSI_STDIO
-[[gnu::format(gnu_printf,2,3)]]
+#ifdef __MINGW32__
+[[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]]
#else
[[gnu::format(printf,2,3)]]
#endif
diff --git a/core/mastering.cpp b/core/mastering.cpp
index 4445719b..9eaabc35 100644
--- a/core/mastering.cpp
+++ b/core/mastering.cpp
@@ -21,8 +21,8 @@
static_assert((BufferLineSize & (BufferLineSize-1)) == 0, "BufferLineSize is not a power of 2");
struct SlidingHold {
- alignas(16) float mValues[BufferLineSize];
- uint mExpiries[BufferLineSize];
+ alignas(16) FloatBufferLine mValues;
+ std::array<uint,BufferLineSize> mExpiries;
uint mLowerIndex;
uint mUpperIndex;
uint mLength;
@@ -44,8 +44,8 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in)
{
static constexpr uint mask{BufferLineSize - 1};
const uint length{Hold->mLength};
- float (&values)[BufferLineSize] = Hold->mValues;
- uint (&expiries)[BufferLineSize] = Hold->mExpiries;
+ const al::span values{Hold->mValues};
+ const al::span expiries{Hold->mExpiries};
uint lowerIndex{Hold->mLowerIndex};
uint upperIndex{Hold->mUpperIndex};
@@ -60,14 +60,16 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in)
}
else
{
- do {
+ auto findLowerIndex = [&lowerIndex,in,values]() noexcept -> bool
+ {
do {
if(!(in >= values[lowerIndex]))
- goto found_place;
+ return true;
} while(lowerIndex--);
+ return false;
+ };
+ while(!findLowerIndex())
lowerIndex = mask;
- } while(true);
- found_place:
lowerIndex = (lowerIndex + 1) & mask;
values[lowerIndex] = in;
@@ -82,38 +84,38 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in)
void ShiftSlidingHold(SlidingHold *Hold, const uint n)
{
- auto exp_begin = std::begin(Hold->mExpiries) + Hold->mUpperIndex;
- auto exp_last = std::begin(Hold->mExpiries) + Hold->mLowerIndex;
- if(exp_last-exp_begin < 0)
+ auto exp_upper = Hold->mExpiries.begin() + Hold->mUpperIndex;
+ if(Hold->mLowerIndex < Hold->mUpperIndex)
{
- std::transform(exp_begin, std::end(Hold->mExpiries), exp_begin,
- [n](uint e){ return e - n; });
- exp_begin = std::begin(Hold->mExpiries);
+ std::transform(exp_upper, Hold->mExpiries.end(), exp_upper,
+ [n](const uint e) noexcept { return e - n; });
+ exp_upper = Hold->mExpiries.begin();
}
- std::transform(exp_begin, exp_last+1, exp_begin, [n](uint e){ return e - n; });
+ const auto exp_lower = Hold->mExpiries.begin() + Hold->mLowerIndex;
+ std::transform(exp_upper, exp_lower+1, exp_upper,
+ [n](const uint e) noexcept { return e - n; });
}
+} // namespace
/* Multichannel compression is linked via the absolute maximum of all
* channels.
*/
-void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLine *OutBuffer)
+void Compressor::linkChannels(const uint SamplesToDo, const FloatBufferLine *OutBuffer)
{
- const size_t numChans{Comp->mNumChans};
-
ASSUME(SamplesToDo > 0);
- ASSUME(numChans > 0);
- auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead;
+ const auto side_begin = mSideChain.begin() + mLookAhead;
std::fill(side_begin, side_begin+SamplesToDo, 0.0f);
auto fill_max = [SamplesToDo,side_begin](const FloatBufferLine &input) -> void
{
const float *RESTRICT buffer{al::assume_aligned<16>(input.data())};
- auto max_abs = std::bind(maxf, _1, std::bind(static_cast<float(&)(float)>(std::fabs), _2));
+ auto max_abs = [](const float s0, const float s1) noexcept -> float
+ { return std::max(s0, std::fabs(s1)); };
std::transform(side_begin, side_begin+SamplesToDo, buffer, side_begin, max_abs);
};
- std::for_each(OutBuffer, OutBuffer+numChans, fill_max);
+ std::for_each(OutBuffer, OutBuffer+mNumChans, fill_max);
}
/* This calculates the squared crest factor of the control signal for the
@@ -121,59 +123,59 @@ void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLin
* it uses an instantaneous squared peak detector and a squared RMS detector
* both with 200ms release times.
*/
-void CrestDetector(Compressor *Comp, const uint SamplesToDo)
+void Compressor::crestDetector(const uint SamplesToDo)
{
- const float a_crest{Comp->mCrestCoeff};
- float y2_peak{Comp->mLastPeakSq};
- float y2_rms{Comp->mLastRmsSq};
+ const float a_crest{mCrestCoeff};
+ float y2_peak{mLastPeakSq};
+ float y2_rms{mLastRmsSq};
ASSUME(SamplesToDo > 0);
auto calc_crest = [&y2_rms,&y2_peak,a_crest](const float x_abs) noexcept -> float
{
- const float x2{clampf(x_abs * x_abs, 0.000001f, 1000000.0f)};
+ const float x2{clampf(x_abs*x_abs, 0.000001f, 1000000.0f)};
- y2_peak = maxf(x2, lerpf(x2, y2_peak, a_crest));
+ y2_peak = std::max(x2, lerpf(x2, y2_peak, a_crest));
y2_rms = lerpf(x2, y2_rms, a_crest);
return y2_peak / y2_rms;
};
- auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead;
- std::transform(side_begin, side_begin+SamplesToDo, std::begin(Comp->mCrestFactor), calc_crest);
+ const auto side_begin = mSideChain.begin() + mLookAhead;
+ std::transform(side_begin, side_begin+SamplesToDo, mCrestFactor.begin(), calc_crest);
- Comp->mLastPeakSq = y2_peak;
- Comp->mLastRmsSq = y2_rms;
+ mLastPeakSq = y2_peak;
+ mLastRmsSq = y2_rms;
}
/* The side-chain starts with a simple peak detector (based on the absolute
* value of the incoming signal) and performs most of its operations in the
* log domain.
*/
-void PeakDetector(Compressor *Comp, const uint SamplesToDo)
+void Compressor::peakDetector(const uint SamplesToDo)
{
ASSUME(SamplesToDo > 0);
- /* Clamp the minimum amplitude to near-zero and convert to logarithm. */
- auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead;
+ /* Clamp the minimum amplitude to near-zero and convert to logarithmic. */
+ const auto side_begin = mSideChain.begin() + mLookAhead;
std::transform(side_begin, side_begin+SamplesToDo, side_begin,
- [](float s) { return std::log(maxf(0.000001f, s)); });
+ [](float s) { return std::log(std::max(0.000001f, s)); });
}
/* An optional hold can be used to extend the peak detector so it can more
* solidly detect fast transients. This is best used when operating as a
* limiter.
*/
-void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo)
+void Compressor::peakHoldDetector(const uint SamplesToDo)
{
ASSUME(SamplesToDo > 0);
- SlidingHold *hold{Comp->mHold};
+ SlidingHold *hold{mHold.get()};
uint i{0};
auto detect_peak = [&i,hold](const float x_abs) -> float
{
- const float x_G{std::log(maxf(0.000001f, x_abs))};
+ const float x_G{std::log(std::max(0.000001f, x_abs))};
return UpdateSlidingHold(hold, i++, x_G);
};
- auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead;
+ auto side_begin = mSideChain.begin() + mLookAhead;
std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak);
ShiftSlidingHold(hold, SamplesToDo);
@@ -184,46 +186,46 @@ void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo)
* to knee width, attack/release times, make-up/post gain, and clipping
* reduction.
*/
-void GainCompressor(Compressor *Comp, const uint SamplesToDo)
+void Compressor::gainCompressor(const uint SamplesToDo)
{
- const bool autoKnee{Comp->mAuto.Knee};
- const bool autoAttack{Comp->mAuto.Attack};
- const bool autoRelease{Comp->mAuto.Release};
- const bool autoPostGain{Comp->mAuto.PostGain};
- const bool autoDeclip{Comp->mAuto.Declip};
- const uint lookAhead{Comp->mLookAhead};
- const float threshold{Comp->mThreshold};
- const float slope{Comp->mSlope};
- const float attack{Comp->mAttack};
- const float release{Comp->mRelease};
- const float c_est{Comp->mGainEstimate};
- const float a_adp{Comp->mAdaptCoeff};
- const float *crestFactor{Comp->mCrestFactor};
- float postGain{Comp->mPostGain};
- float knee{Comp->mKnee};
+ const bool autoKnee{mAuto.Knee};
+ const bool autoAttack{mAuto.Attack};
+ const bool autoRelease{mAuto.Release};
+ const bool autoPostGain{mAuto.PostGain};
+ const bool autoDeclip{mAuto.Declip};
+ const float threshold{mThreshold};
+ const float slope{mSlope};
+ const float attack{mAttack};
+ const float release{mRelease};
+ const float c_est{mGainEstimate};
+ const float a_adp{mAdaptCoeff};
+ auto lookAhead = mSideChain.cbegin() + mLookAhead;
+ auto crestFactor = mCrestFactor.cbegin();
+ float postGain{mPostGain};
+ float knee{mKnee};
float t_att{attack};
float t_rel{release - attack};
float a_att{std::exp(-1.0f / t_att)};
float a_rel{std::exp(-1.0f / t_rel)};
- float y_1{Comp->mLastRelease};
- float y_L{Comp->mLastAttack};
- float c_dev{Comp->mLastGainDev};
+ float y_1{mLastRelease};
+ float y_L{mLastAttack};
+ float c_dev{mLastGainDev};
ASSUME(SamplesToDo > 0);
- for(float &sideChain : al::span<float>{Comp->mSideChain, SamplesToDo})
+ auto process = [&](const float input) -> float
{
if(autoKnee)
- knee = maxf(0.0f, 2.5f * (c_dev + c_est));
+ knee = std::max(0.0f, 2.5f * (c_dev + c_est));
const float knee_h{0.5f * knee};
/* This is the gain computer. It applies a static compression curve
* to the control signal.
*/
- const float x_over{std::addressof(sideChain)[lookAhead] - threshold};
+ const float x_over{*(lookAhead++) - threshold};
const float y_G{
(x_over <= -knee_h) ? 0.0f :
- (std::fabs(x_over) < knee_h) ? (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee) :
+ (std::fabs(x_over) < knee_h) ? (x_over+knee_h) * (x_over+knee_h) / (2.0f * knee) :
x_over};
const float y2_crest{*(crestFactor++)};
@@ -243,7 +245,7 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo)
* above to compensate for the chained operating mode.
*/
const float x_L{-slope * y_G};
- y_1 = maxf(x_L, lerpf(x_L, y_1, a_rel));
+ y_1 = std::max(x_L, lerpf(x_L, y_1, a_rel));
y_L = lerpf(y_1, y_L, a_att);
/* Knee width and make-up gain automation make use of a smoothed
@@ -262,17 +264,19 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo)
* same output level.
*/
if(autoDeclip)
- c_dev = maxf(c_dev, sideChain - y_L - threshold - c_est);
+ c_dev = std::max(c_dev, input - y_L - threshold - c_est);
postGain = -(c_dev + c_est);
}
- sideChain = std::exp(postGain - y_L);
- }
+ return std::exp(postGain - y_L);
+ };
+ auto sideChain = al::span{mSideChain}.first(SamplesToDo);
+ std::transform(sideChain.begin(), sideChain.end(), sideChain.begin(), process);
- Comp->mLastRelease = y_1;
- Comp->mLastAttack = y_L;
- Comp->mLastGainDev = c_dev;
+ mLastRelease = y_1;
+ mLastAttack = y_L;
+ mLastGainDev = c_dev;
}
/* Combined with the hold time, a look-ahead delay can improve handling of
@@ -280,10 +284,10 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo)
* reaching the offending impulse. This is best used when operating as a
* limiter.
*/
-void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutBuffer)
+void Compressor::signalDelay(const uint SamplesToDo, FloatBufferLine *OutBuffer)
{
- const size_t numChans{Comp->mNumChans};
- const uint lookAhead{Comp->mLookAhead};
+ const size_t numChans{mNumChans};
+ const uint lookAhead{mLookAhead};
ASSUME(SamplesToDo > 0);
ASSUME(numChans > 0);
@@ -292,7 +296,7 @@ void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutB
for(size_t c{0};c < numChans;c++)
{
float *inout{al::assume_aligned<16>(OutBuffer[c].data())};
- float *delaybuf{al::assume_aligned<16>(Comp->mDelay[c].data())};
+ float *delaybuf{al::assume_aligned<16>(mDelay[c].data())};
auto inout_end = inout + SamplesToDo;
if(SamplesToDo >= lookAhead) LIKELY
@@ -308,8 +312,6 @@ void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutB
}
}
-} // namespace
-
std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const float SampleRate,
const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, const bool AutoPostGain,
@@ -317,24 +319,12 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa
const float PostGainDb, const float ThresholdDb, const float Ratio, const float KneeDb,
const float AttackTime, const float ReleaseTime)
{
- const auto lookAhead = static_cast<uint>(
- clampf(std::round(LookAheadTime*SampleRate), 0.0f, BufferLineSize-1));
- const auto hold = static_cast<uint>(
- clampf(std::round(HoldTime*SampleRate), 0.0f, BufferLineSize-1));
+ const auto lookAhead = static_cast<uint>(clampf(std::round(LookAheadTime*SampleRate), 0.0f,
+ BufferLineSize-1));
+ const auto hold = static_cast<uint>(clampf(std::round(HoldTime*SampleRate), 0.0f,
+ BufferLineSize-1));
- size_t size{sizeof(Compressor)};
- if(lookAhead > 0)
- {
- size += sizeof(*Compressor::mDelay) * NumChans;
- /* The sliding hold implementation doesn't handle a length of 1. A 1-
- * sample hold is useless anyway, it would only ever give back what was
- * just given to it.
- */
- if(hold > 1)
- size += sizeof(*Compressor::mHold);
- }
-
- auto Comp = CompressorPtr{al::construct_at(static_cast<Compressor*>(al_calloc(16, size)))};
+ auto Comp = CompressorPtr{new Compressor{}};
Comp->mNumChans = NumChans;
Comp->mAuto.Knee = AutoKnee;
Comp->mAuto.Attack = AutoAttack;
@@ -343,12 +333,12 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa
Comp->mAuto.Declip = AutoPostGain && AutoDeclip;
Comp->mLookAhead = lookAhead;
Comp->mPreGain = std::pow(10.0f, PreGainDb / 20.0f);
- Comp->mPostGain = PostGainDb * std::log(10.0f) / 20.0f;
- Comp->mThreshold = ThresholdDb * std::log(10.0f) / 20.0f;
- Comp->mSlope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
- Comp->mKnee = maxf(0.0f, KneeDb * std::log(10.0f) / 20.0f);
- Comp->mAttack = maxf(1.0f, AttackTime * SampleRate);
- Comp->mRelease = maxf(1.0f, ReleaseTime * SampleRate);
+ Comp->mPostGain = std::log(10.0f)/20.0f * PostGainDb;
+ Comp->mThreshold = std::log(10.0f)/20.0f * ThresholdDb;
+ Comp->mSlope = 1.0f / std::max(1.0f, Ratio) - 1.0f;
+ Comp->mKnee = std::max(0.0f, std::log(10.0f)/20.0f * KneeDb);
+ Comp->mAttack = std::max(1.0f, AttackTime * SampleRate);
+ Comp->mRelease = std::max(1.0f, ReleaseTime * SampleRate);
/* Knee width automation actually treats the compressor as a limiter. By
* varying the knee width, it can effectively be seen as applying
@@ -359,17 +349,18 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa
if(lookAhead > 0)
{
+ /* The sliding hold implementation doesn't handle a length of 1. A 1-
+ * sample hold is useless anyway, it would only ever give back what was
+ * just given to it.
+ */
if(hold > 1)
{
- Comp->mHold = al::construct_at(reinterpret_cast<SlidingHold*>(Comp.get() + 1));
+ Comp->mHold = std::make_unique<SlidingHold>();
Comp->mHold->mValues[0] = -std::numeric_limits<float>::infinity();
Comp->mHold->mExpiries[0] = hold;
Comp->mHold->mLength = hold;
- Comp->mDelay = reinterpret_cast<FloatBufferLine*>(Comp->mHold + 1);
}
- else
- Comp->mDelay = reinterpret_cast<FloatBufferLine*>(Comp.get() + 1);
- std::uninitialized_fill_n(Comp->mDelay, NumChans, FloatBufferLine{});
+ Comp->mDelay.resize(NumChans, FloatBufferLine{});
}
Comp->mCrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms
@@ -379,15 +370,7 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa
return Comp;
}
-Compressor::~Compressor()
-{
- if(mHold)
- std::destroy_at(mHold);
- mHold = nullptr;
- if(mDelay)
- std::destroy_n(mDelay, mNumChans);
- mDelay = nullptr;
-}
+Compressor::~Compressor() = default;
void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer)
@@ -404,36 +387,36 @@ void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer)
{
float *buffer{al::assume_aligned<16>(input.data())};
std::transform(buffer, buffer+SamplesToDo, buffer,
- [preGain](float s) { return s * preGain; });
+ [preGain](const float s) noexcept { return s * preGain; });
};
std::for_each(OutBuffer, OutBuffer+numChans, apply_gain);
}
- LinkChannels(this, SamplesToDo, OutBuffer);
+ linkChannels(SamplesToDo, OutBuffer);
if(mAuto.Attack || mAuto.Release)
- CrestDetector(this, SamplesToDo);
+ crestDetector(SamplesToDo);
if(mHold)
- PeakHoldDetector(this, SamplesToDo);
+ peakHoldDetector(SamplesToDo);
else
- PeakDetector(this, SamplesToDo);
+ peakDetector(SamplesToDo);
- GainCompressor(this, SamplesToDo);
+ gainCompressor(SamplesToDo);
- if(mDelay)
- SignalDelay(this, SamplesToDo, OutBuffer);
+ if(!mDelay.empty())
+ signalDelay(SamplesToDo, OutBuffer);
- const float (&sideChain)[BufferLineSize*2] = mSideChain;
- auto apply_comp = [SamplesToDo,&sideChain](FloatBufferLine &input) noexcept -> void
+ const auto sideChain = al::span{mSideChain};
+ auto apply_comp = [SamplesToDo,sideChain](FloatBufferLine &input) noexcept -> void
{
float *buffer{al::assume_aligned<16>(input.data())};
- const float *gains{al::assume_aligned<16>(&sideChain[0])};
+ const float *gains{al::assume_aligned<16>(sideChain.data())};
std::transform(gains, gains+SamplesToDo, buffer, buffer,
- [](float g, float s) { return g * s; });
+ [](const float g, const float s) noexcept { return g * s; });
};
std::for_each(OutBuffer, OutBuffer+numChans, apply_comp);
- auto side_begin = std::begin(mSideChain) + SamplesToDo;
- std::copy(side_begin, side_begin+mLookAhead, std::begin(mSideChain));
+ auto side_begin = mSideChain.begin() + SamplesToDo;
+ std::copy(side_begin, side_begin+mLookAhead, mSideChain.begin());
}
diff --git a/core/mastering.h b/core/mastering.h
index 1a36937c..ca7266a3 100644
--- a/core/mastering.h
+++ b/core/mastering.h
@@ -1,10 +1,14 @@
#ifndef CORE_MASTERING_H
#define CORE_MASTERING_H
+#include <array>
#include <memory>
#include "almalloc.h"
+#include "alnumeric.h"
+#include "alspan.h"
#include "bufferline.h"
+#include "vector.h"
struct SlidingHold;
@@ -21,7 +25,7 @@ using uint = unsigned int;
*
* http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
*/
-struct Compressor {
+class Compressor {
size_t mNumChans{0u};
struct {
@@ -44,11 +48,11 @@ struct Compressor {
float mAttack{0.0f};
float mRelease{0.0f};
- alignas(16) float mSideChain[2*BufferLineSize]{};
- alignas(16) float mCrestFactor[BufferLineSize]{};
+ alignas(16) std::array<float,BufferLineSize*2_uz> mSideChain{};
+ alignas(16) std::array<float,BufferLineSize> mCrestFactor{};
- SlidingHold *mHold{nullptr};
- FloatBufferLine *mDelay{nullptr};
+ std::unique_ptr<SlidingHold> mHold;
+ al::vector<FloatBufferLine,16> mDelay;
float mCrestCoeff{0.0f};
float mGainEstimate{0.0f};
@@ -60,12 +64,19 @@ struct Compressor {
float mLastAttack{0.0f};
float mLastGainDev{0.0f};
+ Compressor() = default;
+ void linkChannels(const uint SamplesToDo, const FloatBufferLine *OutBuffer);
+ void crestDetector(const uint SamplesToDo);
+ void peakDetector(const uint SamplesToDo);
+ void peakHoldDetector(const uint SamplesToDo);
+ void gainCompressor(const uint SamplesToDo);
+ void signalDelay(const uint SamplesToDo, FloatBufferLine *OutBuffer);
+
+public:
~Compressor();
void process(const uint SamplesToDo, FloatBufferLine *OutBuffer);
- int getLookAhead() const noexcept { return static_cast<int>(mLookAhead); }
-
- DEF_PLACE_NEWDEL()
+ [[nodiscard]] auto getLookAhead() const noexcept -> uint { return mLookAhead; }
/**
* The compressor is initialized with the following settings:
diff --git a/core/mixer/defs.h b/core/mixer/defs.h
index 48daca9b..4d0d19bf 100644
--- a/core/mixer/defs.h
+++ b/core/mixer/defs.h
@@ -2,7 +2,8 @@
#define CORE_MIXER_DEFS_H
#include <array>
-#include <stdlib.h>
+#include <cstdlib>
+#include <variant>
#include "alspan.h"
#include "core/bufferline.h"
@@ -17,12 +18,12 @@ using uint = unsigned int;
using float2 = std::array<float,2>;
-constexpr int MixerFracBits{16};
-constexpr int MixerFracOne{1 << MixerFracBits};
-constexpr int MixerFracMask{MixerFracOne - 1};
-constexpr int MixerFracHalf{MixerFracOne >> 1};
+inline constexpr int MixerFracBits{16};
+inline constexpr int MixerFracOne{1 << MixerFracBits};
+inline constexpr int MixerFracMask{MixerFracOne - 1};
+inline constexpr int MixerFracHalf{MixerFracOne >> 1};
-constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */
+inline constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */
enum class Resampler : uint8_t {
@@ -59,10 +60,7 @@ struct CubicState {
const CubicCoefficients *filter;
};
-union InterpState {
- CubicState cubic;
- BsincState bsinc;
-};
+using InterpState = std::variant<CubicState,BsincState>;
using ResamplerFunc = void(*)(const InterpState *state, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst);
@@ -94,7 +92,8 @@ void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOu
/* Vectorized resampler helpers */
template<size_t N>
-inline void InitPosArrays(uint frac, uint increment, uint (&frac_arr)[N], uint (&pos_arr)[N])
+inline void InitPosArrays(uint frac, uint increment, const al::span<uint,N> frac_arr,
+ const al::span<uint,N> pos_arr)
{
pos_arr[0] = 0;
frac_arr[0] = frac;
diff --git a/core/mixer/mixer_c.cpp b/core/mixer/mixer_c.cpp
index 28a92ef7..93306bba 100644
--- a/core/mixer/mixer_c.cpp
+++ b/core/mixer/mixer_c.cpp
@@ -28,53 +28,53 @@ constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits};
constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits};
constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u};
-inline float do_point(const InterpState&, const float *RESTRICT vals, const uint)
+inline float do_point(const float *RESTRICT vals, const uint)
{ return vals[0]; }
-inline float do_lerp(const InterpState&, const float *RESTRICT vals, const uint frac)
+inline float do_lerp(const float *RESTRICT vals, const uint frac)
{ return lerpf(vals[0], vals[1], static_cast<float>(frac)*(1.0f/MixerFracOne)); }
-inline float do_cubic(const InterpState &istate, const float *RESTRICT vals, const uint frac)
+inline float do_cubic(const CubicState &istate, const float *RESTRICT vals, const uint frac)
{
/* Calculate the phase index and factor. */
const uint pi{frac >> CubicPhaseDiffBits};
const float pf{static_cast<float>(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)};
- const float *RESTRICT fil{al::assume_aligned<16>(istate.cubic.filter[pi].mCoeffs)};
- const float *RESTRICT phd{al::assume_aligned<16>(istate.cubic.filter[pi].mDeltas)};
+ const float *RESTRICT fil{al::assume_aligned<16>(istate.filter[pi].mCoeffs.data())};
+ const float *RESTRICT phd{al::assume_aligned<16>(istate.filter[pi].mDeltas.data())};
/* Apply the phase interpolated filter. */
return (fil[0] + pf*phd[0])*vals[0] + (fil[1] + pf*phd[1])*vals[1]
+ (fil[2] + pf*phd[2])*vals[2] + (fil[3] + pf*phd[3])*vals[3];
}
-inline float do_bsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac)
+inline float do_bsinc(const BsincState &istate, const float *RESTRICT vals, const uint frac)
{
- const size_t m{istate.bsinc.m};
+ const size_t m{istate.m};
ASSUME(m > 0);
/* Calculate the phase index and factor. */
const uint pi{frac >> BsincPhaseDiffBits};
const float pf{static_cast<float>(frac&BsincPhaseDiffMask) * (1.0f/BsincPhaseDiffOne)};
- const float *RESTRICT fil{istate.bsinc.filter + m*pi*2};
+ const float *RESTRICT fil{istate.filter + m*pi*2_uz};
const float *RESTRICT phd{fil + m};
- const float *RESTRICT scd{fil + BSincPhaseCount*2*m};
+ const float *RESTRICT scd{fil + BSincPhaseCount*2_uz*m};
const float *RESTRICT spd{scd + m};
/* Apply the scale and phase interpolated filter. */
float r{0.0f};
for(size_t j_f{0};j_f < m;j_f++)
- r += (fil[j_f] + istate.bsinc.sf*scd[j_f] + pf*(phd[j_f] + istate.bsinc.sf*spd[j_f])) * vals[j_f];
+ r += (fil[j_f] + istate.sf*scd[j_f] + pf*(phd[j_f] + istate.sf*spd[j_f])) * vals[j_f];
return r;
}
-inline float do_fastbsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac)
+inline float do_fastbsinc(const BsincState &istate, const float *RESTRICT vals, const uint frac)
{
- const size_t m{istate.bsinc.m};
+ const size_t m{istate.m};
ASSUME(m > 0);
/* Calculate the phase index and factor. */
const uint pi{frac >> BsincPhaseDiffBits};
const float pf{static_cast<float>(frac&BsincPhaseDiffMask) * (1.0f/BsincPhaseDiffOne)};
- const float *RESTRICT fil{istate.bsinc.filter + m*pi*2};
+ const float *RESTRICT fil{istate.filter + m*pi*2_uz};
const float *RESTRICT phd{fil + m};
/* Apply the phase interpolated filter. */
@@ -84,16 +84,30 @@ inline float do_fastbsinc(const InterpState &istate, const float *RESTRICT vals,
return r;
}
-using SamplerT = float(&)(const InterpState&, const float*RESTRICT, const uint);
+using SamplerT = float(&)(const float*RESTRICT, const uint);
template<SamplerT Sampler>
-void DoResample(const InterpState *state, const float *RESTRICT src, uint frac,
+void DoResample(const float *RESTRICT src, uint frac, const uint increment,
+ const al::span<float> dst)
+{
+ ASSUME(frac < MixerFracOne);
+ for(float &out : dst)
+ {
+ out = Sampler(src, frac);
+
+ frac += increment;
+ src += frac>>MixerFracBits;
+ frac &= MixerFracMask;
+ }
+}
+
+template<typename T, typename U>
+void DoResample(T sampler, const U istate, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst)
{
- const InterpState istate{*state};
ASSUME(frac < MixerFracOne);
for(float &out : dst)
{
- out = Sampler(istate, src, frac);
+ out = sampler(istate, src, frac);
frac += increment;
src += frac>>MixerFracBits;
@@ -146,29 +160,35 @@ force_inline void MixLine(const al::span<const float> InSamples, float *RESTRICT
} // namespace
template<>
-void Resample_<PointTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac,
+void Resample_<PointTag,CTag>(const InterpState*, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst)
-{ DoResample<do_point>(state, src, frac, increment, dst); }
+{ DoResample<do_point>(src, frac, increment, dst); }
template<>
-void Resample_<LerpTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac,
+void Resample_<LerpTag,CTag>(const InterpState*, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst)
-{ DoResample<do_lerp>(state, src, frac, increment, dst); }
+{ DoResample<do_lerp>(src, frac, increment, dst); }
template<>
void Resample_<CubicTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst)
-{ DoResample<do_cubic>(state, src-1, frac, increment, dst); }
+{ DoResample(do_cubic, std::get<CubicState>(*state), src-1, frac, increment, dst); }
template<>
void Resample_<BSincTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst)
-{ DoResample<do_bsinc>(state, src-state->bsinc.l, frac, increment, dst); }
+{
+ const auto istate = std::get<BsincState>(*state);
+ DoResample(do_bsinc, istate, src-istate.l, frac, increment, dst);
+}
template<>
void Resample_<FastBSincTag,CTag>(const InterpState *state, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst)
-{ DoResample<do_fastbsinc>(state, src-state->bsinc.l, frac, increment, dst); }
+{
+ const auto istate = std::get<BsincState>(*state);
+ DoResample(do_fastbsinc, istate, src-istate.l, frac, increment, dst);
+}
template<>
diff --git a/core/mixer/mixer_neon.cpp b/core/mixer/mixer_neon.cpp
index ead775af..9fa2425f 100644
--- a/core/mixer/mixer_neon.cpp
+++ b/core/mixer/mixer_neon.cpp
@@ -146,12 +146,11 @@ void Resample_<LerpTag,NEONTag>(const InterpState*, const float *RESTRICT src, u
const int32x4_t increment4 = vdupq_n_s32(static_cast<int>(increment*4));
const float32x4_t fracOne4 = vdupq_n_f32(1.0f/MixerFracOne);
const int32x4_t fracMask4 = vdupq_n_s32(MixerFracMask);
- alignas(16) uint pos_[4], frac_[4];
- int32x4_t pos4, frac4;
- InitPosArrays(frac, increment, frac_, pos_);
- frac4 = vld1q_s32(reinterpret_cast<int*>(frac_));
- pos4 = vld1q_s32(reinterpret_cast<int*>(pos_));
+ alignas(16) std::array<uint,4> pos_, frac_;
+ InitPosArrays(frac, increment, al::span{frac_}, al::span{pos_});
+ int32x4_t frac4 = vld1q_s32(reinterpret_cast<int*>(frac_.data()));
+ int32x4_t pos4 = vld1q_s32(reinterpret_cast<int*>(pos_.data()));
auto dst_iter = dst.begin();
for(size_t todo{dst.size()>>2};todo;--todo)
@@ -197,7 +196,7 @@ void Resample_<CubicTag,NEONTag>(const InterpState *state, const float *RESTRICT
{
ASSUME(frac < MixerFracOne);
- const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter);
+ const auto *RESTRICT filter = al::assume_aligned<16>(std::get<CubicState>(*state).filter);
src -= 1;
for(float &out_sample : dst)
@@ -209,8 +208,8 @@ void Resample_<CubicTag,NEONTag>(const InterpState *state, const float *RESTRICT
/* Apply the phase interpolated filter. */
/* f = fil + pf*phd */
- const float32x4_t f4 = vmlaq_f32(vld1q_f32(filter[pi].mCoeffs), pf4,
- vld1q_f32(filter[pi].mDeltas));
+ const float32x4_t f4 = vmlaq_f32(vld1q_f32(filter[pi].mCoeffs.data()), pf4,
+ vld1q_f32(filter[pi].mDeltas.data()));
/* r = f*src */
float32x4_t r4{vmulq_f32(f4, vld1q_f32(src))};
@@ -227,13 +226,14 @@ template<>
void Resample_<BSincTag,NEONTag>(const InterpState *state, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst)
{
- const float *const filter{state->bsinc.filter};
- const float32x4_t sf4{vdupq_n_f32(state->bsinc.sf)};
- const size_t m{state->bsinc.m};
+ const auto &bsinc = std::get<BsincState>(*state);
+ const float *const filter{bsinc.filter};
+ const float32x4_t sf4{vdupq_n_f32(bsinc.sf)};
+ const size_t m{bsinc.m};
ASSUME(m > 0);
ASSUME(frac < MixerFracOne);
- src -= state->bsinc.l;
+ src -= bsinc.l;
for(float &out_sample : dst)
{
// Calculate the phase index and factor.
@@ -244,9 +244,9 @@ void Resample_<BSincTag,NEONTag>(const InterpState *state, const float *RESTRICT
float32x4_t r4{vdupq_n_f32(0.0f)};
{
const float32x4_t pf4{vdupq_n_f32(pf)};
- const float *RESTRICT fil{filter + m*pi*2};
+ const float *RESTRICT fil{filter + m*pi*2_uz};
const float *RESTRICT phd{fil + m};
- const float *RESTRICT scd{fil + BSincPhaseCount*2*m};
+ const float *RESTRICT scd{fil + BSincPhaseCount*2_uz*m};
const float *RESTRICT spd{scd + m};
size_t td{m >> 2};
size_t j{0u};
@@ -271,15 +271,16 @@ void Resample_<BSincTag,NEONTag>(const InterpState *state, const float *RESTRICT
}
template<>
-void Resample_<FastBSincTag,NEONTag>(const InterpState *state, const float *RESTRICT src, uint frac,
- const uint increment, const al::span<float> dst)
+void Resample_<FastBSincTag,NEONTag>(const InterpState *state, const float *RESTRICT src,
+ uint frac, const uint increment, const al::span<float> dst)
{
- const float *const filter{state->bsinc.filter};
- const size_t m{state->bsinc.m};
+ const auto &bsinc = std::get<BsincState>(*state);
+ const float *const filter{bsinc.filter};
+ const size_t m{bsinc.m};
ASSUME(m > 0);
ASSUME(frac < MixerFracOne);
- src -= state->bsinc.l;
+ src -= bsinc.l;
for(float &out_sample : dst)
{
// Calculate the phase index and factor.
@@ -290,7 +291,7 @@ void Resample_<FastBSincTag,NEONTag>(const InterpState *state, const float *REST
float32x4_t r4{vdupq_n_f32(0.0f)};
{
const float32x4_t pf4{vdupq_n_f32(pf)};
- const float *RESTRICT fil{filter + m*pi*2};
+ const float *RESTRICT fil{filter + m*pi*2_uz};
const float *RESTRICT phd{fil + m};
size_t td{m >> 2};
size_t j{0u};
diff --git a/core/mixer/mixer_sse.cpp b/core/mixer/mixer_sse.cpp
index 70f77c14..809d585d 100644
--- a/core/mixer/mixer_sse.cpp
+++ b/core/mixer/mixer_sse.cpp
@@ -159,7 +159,7 @@ void Resample_<CubicTag,SSETag>(const InterpState *state, const float *RESTRICT
{
ASSUME(frac < MixerFracOne);
- const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter);
+ const auto *RESTRICT filter = al::assume_aligned<16>(std::get<CubicState>(*state).filter);
src -= 1;
for(float &out_sample : dst)
@@ -171,8 +171,8 @@ void Resample_<CubicTag,SSETag>(const InterpState *state, const float *RESTRICT
/* Apply the phase interpolated filter. */
/* f = fil + pf*phd */
- const __m128 f4 = MLA4(_mm_load_ps(filter[pi].mCoeffs), pf4,
- _mm_load_ps(filter[pi].mDeltas));
+ const __m128 f4 = MLA4(_mm_load_ps(filter[pi].mCoeffs.data()), pf4,
+ _mm_load_ps(filter[pi].mDeltas.data()));
/* r = f*src */
__m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(src))};
@@ -190,13 +190,14 @@ template<>
void Resample_<BSincTag,SSETag>(const InterpState *state, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst)
{
- const float *const filter{state->bsinc.filter};
- const __m128 sf4{_mm_set1_ps(state->bsinc.sf)};
- const size_t m{state->bsinc.m};
+ const auto &bsinc = std::get<BsincState>(*state);
+ const float *const filter{bsinc.filter};
+ const __m128 sf4{_mm_set1_ps(bsinc.sf)};
+ const size_t m{bsinc.m};
ASSUME(m > 0);
ASSUME(frac < MixerFracOne);
- src -= state->bsinc.l;
+ src -= bsinc.l;
for(float &out_sample : dst)
{
// Calculate the phase index and factor.
@@ -207,9 +208,9 @@ void Resample_<BSincTag,SSETag>(const InterpState *state, const float *RESTRICT
__m128 r4{_mm_setzero_ps()};
{
const __m128 pf4{_mm_set1_ps(pf)};
- const float *RESTRICT fil{filter + m*pi*2};
+ const float *RESTRICT fil{filter + m*pi*2_uz};
const float *RESTRICT phd{fil + m};
- const float *RESTRICT scd{fil + BSincPhaseCount*2*m};
+ const float *RESTRICT scd{fil + BSincPhaseCount*2_uz*m};
const float *RESTRICT spd{scd + m};
size_t td{m >> 2};
size_t j{0u};
@@ -238,12 +239,13 @@ template<>
void Resample_<FastBSincTag,SSETag>(const InterpState *state, const float *RESTRICT src, uint frac,
const uint increment, const al::span<float> dst)
{
- const float *const filter{state->bsinc.filter};
- const size_t m{state->bsinc.m};
+ const auto &bsinc = std::get<BsincState>(*state);
+ const float *const filter{bsinc.filter};
+ const size_t m{bsinc.m};
ASSUME(m > 0);
ASSUME(frac < MixerFracOne);
- src -= state->bsinc.l;
+ src -= bsinc.l;
for(float &out_sample : dst)
{
// Calculate the phase index and factor.
@@ -254,7 +256,7 @@ void Resample_<FastBSincTag,SSETag>(const InterpState *state, const float *RESTR
__m128 r4{_mm_setzero_ps()};
{
const __m128 pf4{_mm_set1_ps(pf)};
- const float *RESTRICT fil{filter + m*pi*2};
+ const float *RESTRICT fil{filter + m*pi*2_uz};
const float *RESTRICT phd{fil + m};
size_t td{m >> 2};
size_t j{0u};
diff --git a/core/mixer/mixer_sse2.cpp b/core/mixer/mixer_sse2.cpp
index edaaf7a1..aa08b7ed 100644
--- a/core/mixer/mixer_sse2.cpp
+++ b/core/mixer/mixer_sse2.cpp
@@ -44,8 +44,8 @@ void Resample_<LerpTag,SSE2Tag>(const InterpState*, const float *RESTRICT src, u
const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)};
const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)};
- alignas(16) uint pos_[4], frac_[4];
- InitPosArrays(frac, increment, frac_, pos_);
+ alignas(16) std::array<uint,4> pos_, frac_;
+ InitPosArrays(frac, increment, al::span{frac_}, al::span{pos_});
__m128i frac4{_mm_setr_epi32(static_cast<int>(frac_[0]), static_cast<int>(frac_[1]),
static_cast<int>(frac_[2]), static_cast<int>(frac_[3]))};
__m128i pos4{_mm_setr_epi32(static_cast<int>(pos_[0]), static_cast<int>(pos_[1]),
diff --git a/core/mixer/mixer_sse41.cpp b/core/mixer/mixer_sse41.cpp
index 8ccd9fd3..d66f9ce5 100644
--- a/core/mixer/mixer_sse41.cpp
+++ b/core/mixer/mixer_sse41.cpp
@@ -45,8 +45,8 @@ void Resample_<LerpTag,SSE4Tag>(const InterpState*, const float *RESTRICT src, u
const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)};
const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)};
- alignas(16) uint pos_[4], frac_[4];
- InitPosArrays(frac, increment, frac_, pos_);
+ alignas(16) std::array<uint,4> pos_, frac_;
+ InitPosArrays(frac, increment, al::span{frac_}, al::span{pos_});
__m128i frac4{_mm_setr_epi32(static_cast<int>(frac_[0]), static_cast<int>(frac_[1]),
static_cast<int>(frac_[2]), static_cast<int>(frac_[3]))};
__m128i pos4{_mm_setr_epi32(static_cast<int>(pos_[0]), static_cast<int>(pos_[1]),
diff --git a/core/rtkit.cpp b/core/rtkit.cpp
index ff944ebf..73ea132f 100644
--- a/core/rtkit.cpp
+++ b/core/rtkit.cpp
@@ -30,14 +30,14 @@
#include "rtkit.h"
-#include <errno.h>
+#include <cerrno>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <memory>
-#include <string.h>
+#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#ifdef __linux__
diff --git a/core/uhjfilter.cpp b/core/uhjfilter.cpp
index 28999e09..681b0abc 100644
--- a/core/uhjfilter.cpp
+++ b/core/uhjfilter.cpp
@@ -5,6 +5,7 @@
#include <algorithm>
#include <iterator>
+#include <vector>
#include "alcomplex.h"
#include "alnumeric.h"
@@ -20,11 +21,6 @@ UhjQualityType UhjEncodeQuality{UhjQualityType::Default};
namespace {
-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
@@ -57,13 +53,11 @@ struct SegmentedFilter {
static_assert(N >= sFftLength);
static_assert((N % sSampleLength) == 0);
- PFFFTSetupPtr mFft;
+ PFFFTSetup mFft;
alignas(16) std::array<float,sFftLength*sNumSegments> mFilterData;
- SegmentedFilter()
+ SegmentedFilter() : mFft{sFftLength, PFFFT_REAL}
{
- 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};
@@ -71,8 +65,7 @@ struct SegmentedFilter {
/* 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{});
+ auto fftBuffer = std::vector<complex_d>(fft_size, complex_d{});
fftBuffer[half_size] = 1.0;
/* Convert to the frequency domain, shift the phase of each bin by +90
@@ -82,27 +75,27 @@ struct SegmentedFilter {
* 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});
+ forward_fft(al::span{fftBuffer});
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});
+ inverse_fft(al::span{fftBuffer});
/* 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 fftBuffer2 = std::vector<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});
+ std::fill_n(fftBuffer2.data()+sSampleLength, sSampleLength, complex_d{});
+ forward_fft(al::span{fftBuffer2});
/* Convert to zdomain data for PFFFT, scaled by the FFT length so
* the iFFT result will be normalized.
@@ -113,7 +106,7 @@ struct SegmentedFilter {
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);
+ mFft.zreorder(fftTmp.data(), filter, PFFFT_BACKWARD);
filter += sFftLength;
}
}
@@ -246,7 +239,7 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
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);
+ Filter.mFft.transform(input, input, mWorkData.data(), PFFFT_FORWARD);
/* Convolve each input segment with its IR filter counterpart (aligned
* in time, from newest to oldest).
@@ -255,14 +248,14 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
const float *filter{Filter.mFilterData.data()};
for(size_t s{curseg};s < sNumSegments;++s)
{
- pffft_zconvolve_accumulate(Filter.mFft.get(), input, filter, mFftBuffer.data());
+ Filter.mFft.zconvolve_accumulate(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());
+ Filter.mFft.zconvolve_accumulate(input, filter, mFftBuffer.data());
input += sFftLength;
filter += sFftLength;
}
@@ -270,8 +263,8 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
/* 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);
+ Filter.mFft.transform(mFftBuffer.data(), mFftBuffer.data(), mWorkData.data(),
+ PFFFT_BACKWARD);
for(size_t i{0};i < sSegmentSize;++i)
mWXInOut[i] = mFftBuffer[i] + mWXInOut[sSegmentSize+i];
diff --git a/core/uhjfilter.h b/core/uhjfilter.h
index 348dc7e1..74ff2167 100644
--- a/core/uhjfilter.h
+++ b/core/uhjfilter.h
@@ -25,7 +25,7 @@ extern UhjQualityType UhjEncodeQuality;
struct UhjAllPassFilter {
struct AllPassState {
/* Last two delayed components for direct form II. */
- float z[2];
+ std::array<float,2> z{};
};
std::array<AllPassState,4> mState;
@@ -110,8 +110,6 @@ struct UhjEncoderIIR final : public UhjEncoderBase {
*/
void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples,
const size_t SamplesToDo) override;
-
- DEF_NEWDEL(UhjEncoderIIR)
};
@@ -158,8 +156,6 @@ struct UhjDecoder final : public DecoderBase {
*/
void decode(const al::span<float*> samples, const size_t samplesToDo,
const bool updateState) override;
-
- DEF_NEWDEL(UhjDecoder)
};
struct UhjDecoderIIR final : public DecoderBase {
@@ -184,8 +180,6 @@ struct UhjDecoderIIR final : public DecoderBase {
void decode(const al::span<float*> samples, const size_t samplesToDo,
const bool updateState) override;
-
- DEF_NEWDEL(UhjDecoderIIR)
};
template<size_t N>
@@ -210,8 +204,6 @@ struct UhjStereoDecoder final : public DecoderBase {
*/
void decode(const al::span<float*> samples, const size_t samplesToDo,
const bool updateState) override;
-
- DEF_NEWDEL(UhjStereoDecoder)
};
struct UhjStereoDecoderIIR final : public DecoderBase {
@@ -231,8 +223,6 @@ struct UhjStereoDecoderIIR final : public DecoderBase {
void decode(const al::span<float*> samples, const size_t samplesToDo,
const bool updateState) override;
-
- DEF_NEWDEL(UhjStereoDecoderIIR)
};
#endif /* CORE_UHJFILTER_H */
diff --git a/core/voice.cpp b/core/voice.cpp
index 3889c42d..4a30ee83 100644
--- a/core/voice.cpp
+++ b/core/voice.cpp
@@ -9,11 +9,11 @@
#include <cassert>
#include <climits>
#include <cstdint>
+#include <cstdlib>
#include <iterator>
#include <memory>
#include <new>
#include <optional>
-#include <stdlib.h>
#include <utility>
#include <vector>
@@ -133,18 +133,18 @@ void Voice::InitMixer(std::optional<std::string> resampler)
if(resampler)
{
struct ResamplerEntry {
- const char name[16];
+ const char *name;
const Resampler resampler;
};
- constexpr ResamplerEntry ResamplerList[]{
- { "none", Resampler::Point },
- { "point", Resampler::Point },
- { "linear", Resampler::Linear },
- { "cubic", Resampler::Cubic },
- { "bsinc12", Resampler::BSinc12 },
- { "fast_bsinc12", Resampler::FastBSinc12 },
- { "bsinc24", Resampler::BSinc24 },
- { "fast_bsinc24", Resampler::FastBSinc24 },
+ constexpr std::array ResamplerList{
+ ResamplerEntry{"none", Resampler::Point},
+ ResamplerEntry{"point", Resampler::Point},
+ ResamplerEntry{"linear", Resampler::Linear},
+ ResamplerEntry{"cubic", Resampler::Cubic},
+ ResamplerEntry{"bsinc12", Resampler::BSinc12},
+ ResamplerEntry{"fast_bsinc12", Resampler::FastBSinc12},
+ ResamplerEntry{"bsinc24", Resampler::BSinc24},
+ ResamplerEntry{"fast_bsinc24", Resampler::FastBSinc24},
};
const char *str{resampler->c_str()};
@@ -159,10 +159,10 @@ void Voice::InitMixer(std::optional<std::string> resampler)
str = "cubic";
}
- auto iter = std::find_if(std::begin(ResamplerList), std::end(ResamplerList),
+ auto iter = std::find_if(ResamplerList.begin(), ResamplerList.end(),
[str](const ResamplerEntry &entry) -> bool
{ return al::strcasecmp(str, entry.name) == 0; });
- if(iter == std::end(ResamplerList))
+ if(iter == ResamplerList.end())
ERR("Invalid resampler: %s\n", str);
else
ResamplerDefault = iter->resampler;
@@ -178,7 +178,7 @@ void Voice::InitMixer(std::optional<std::string> resampler)
namespace {
/* IMA ADPCM Stepsize table */
-constexpr int IMAStep_size[89] = {
+constexpr std::array<int,89> IMAStep_size{{
7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19,
21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55,
60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157,
@@ -188,35 +188,35 @@ constexpr int IMAStep_size[89] = {
4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442,
11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794,
32767
-};
+}};
/* IMA4 ADPCM Codeword decode table */
-constexpr int IMA4Codeword[16] = {
+constexpr std::array<int,16> IMA4Codeword{{
1, 3, 5, 7, 9, 11, 13, 15,
-1,-3,-5,-7,-9,-11,-13,-15,
-};
+}};
/* IMA4 ADPCM Step index adjust decode table */
-constexpr int IMA4Index_adjust[16] = {
+constexpr std::array<int,16>IMA4Index_adjust{{
-1,-1,-1,-1, 2, 4, 6, 8,
-1,-1,-1,-1, 2, 4, 6, 8
-};
+}};
/* MSADPCM Adaption table */
-constexpr int MSADPCMAdaption[16] = {
+constexpr std::array<int,16> MSADPCMAdaption{{
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
-};
+}};
/* MSADPCM Adaption Coefficient tables */
-constexpr int MSADPCMAdaptionCoeff[7][2] = {
- { 256, 0 },
- { 512, -256 },
- { 0, 0 },
- { 192, 64 },
- { 240, 0 },
- { 460, -208 },
- { 392, -232 }
+constexpr std::array MSADPCMAdaptionCoeff{
+ std::array{256, 0},
+ std::array{512, -256},
+ std::array{ 0, 0},
+ std::array{192, 64},
+ std::array{240, 0},
+ std::array{460, -208},
+ std::array{392, -232}
};
@@ -307,7 +307,7 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const std::byte *sr
auto decode_sample = [&sample,&index](const uint nibble)
{
- sample += IMA4Codeword[nibble] * IMAStep_size[index] / 8;
+ sample += IMA4Codeword[nibble] * IMAStep_size[static_cast<uint>(index)] / 8;
sample = clampi(sample, -32768, 32767);
index += IMA4Index_adjust[nibble];
@@ -382,7 +382,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const std::byte
int delta{int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1]) << 8)};
input += srcStep*2;
- int sampleHistory[2]{};
+ std::array<int,2> sampleHistory{};
sampleHistory[0] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8);
input += srcStep*2;
sampleHistory[1] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8);
@@ -421,7 +421,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const std::byte
sampleHistory[1] = sampleHistory[0];
sampleHistory[0] = pred;
- delta = (MSADPCMAdaption[nibble] * delta) / 256;
+ delta = (MSADPCMAdaption[static_cast<uint>(nibble)] * delta) / 256;
delta = maxi(16, delta);
return pred;
@@ -630,8 +630,8 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par
parms.Hrtf.Target.Coeffs,
parms.Hrtf.Target.Delay,
0.0f, gain / static_cast<float>(fademix)};
- MixHrtfBlendSamples(HrtfSamples, AccumSamples+OutPos, IrSize, &parms.Hrtf.Old, &hrtfparams,
- fademix);
+ MixHrtfBlendSamples(HrtfSamples.data(), AccumSamples.data()+OutPos, IrSize,
+ &parms.Hrtf.Old, &hrtfparams, fademix);
/* Update the old parameters with the result. */
parms.Hrtf.Old = parms.Hrtf.Target;
@@ -658,7 +658,8 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par
parms.Hrtf.Target.Delay,
parms.Hrtf.Old.Gain,
(gain - parms.Hrtf.Old.Gain) / static_cast<float>(todo)};
- MixHrtfSamples(HrtfSamples+fademix, AccumSamples+OutPos, IrSize, &hrtfparams, todo);
+ MixHrtfSamples(HrtfSamples.data()+fademix, AccumSamples.data()+OutPos, IrSize, &hrtfparams,
+ todo);
/* Store the now-current gain for next time. */
parms.Hrtf.Old.Gain = gain;
@@ -669,8 +670,8 @@ void DoNfcMix(const al::span<const float> samples, FloatBufferLine *OutBuffer, D
const float *TargetGains, const uint Counter, const uint OutPos, DeviceBase *Device)
{
using FilterProc = void (NfcFilter::*)(const al::span<const float>, float*);
- static constexpr FilterProc NfcProcess[MaxAmbiOrder+1]{
- nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3};
+ static constexpr std::array<FilterProc,MaxAmbiOrder+1> NfcProcess{{
+ nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}};
float *CurrentGains{parms.Gains.Current.data()};
MixSamples(samples, {OutBuffer, 1u}, CurrentGains, TargetGains, Counter, OutPos);
@@ -678,7 +679,7 @@ void DoNfcMix(const al::span<const float> samples, FloatBufferLine *OutBuffer, D
++CurrentGains;
++TargetGains;
- const al::span<float> nfcsamples{Device->NfcSampleData, samples.size()};
+ const al::span<float> nfcsamples{Device->NfcSampleData.data(), samples.size()};
size_t order{1};
while(const size_t chancount{Device->NumChannelsPerOrder[order]})
{
@@ -697,7 +698,7 @@ void DoNfcMix(const al::span<const float> samples, FloatBufferLine *OutBuffer, D
void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds deviceTime,
const uint SamplesToDo)
{
- static constexpr std::array<float,MAX_OUTPUT_CHANNELS> SilentTarget{};
+ static constexpr std::array<float,MaxOutputChannels> SilentTarget{};
ASSUME(SamplesToDo > 0);
@@ -903,8 +904,8 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
const size_t needBlocks{(needSamples + mSamplesPerBlock-1) / mSamplesPerBlock};
if(!mFlags.test(VoiceCallbackStopped) && needBlocks > mNumCallbackBlocks)
{
- const size_t byteOffset{mNumCallbackBlocks*mBytesPerBlock};
- const size_t needBytes{(needBlocks-mNumCallbackBlocks)*mBytesPerBlock};
+ const size_t byteOffset{mNumCallbackBlocks*size_t{mBytesPerBlock}};
+ const size_t needBytes{(needBlocks-mNumCallbackBlocks)*size_t{mBytesPerBlock}};
const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData,
&BufferListItem->mSamples[byteOffset], static_cast<int>(needBytes))};
@@ -918,7 +919,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
else
mNumCallbackBlocks = static_cast<uint>(needBlocks);
}
- const size_t numSamples{uint{mNumCallbackBlocks} * mSamplesPerBlock};
+ const size_t numSamples{size_t{mNumCallbackBlocks} * mSamplesPerBlock};
LoadBufferCallback(BufferListItem, bufferOffset, numSamples, mFmtType, chan,
mFrameStep, srcSampleDelay, srcBufferSize, al::to_address(resampleBuffer));
}
@@ -953,7 +954,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
fracPos += dstBufferSize*increment;
const uint srcOffset{fracPos >> MixerFracBits};
fracPos &= MixerFracMask;
- intPos += srcOffset;
+ intPos += static_cast<int>(srcOffset);
/* If more samples need to be loaded, copy the back of the
* resampleBuffer to the front to reuse it. prevSamples isn't
@@ -1017,7 +1018,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
if(mFlags.test(VoiceHasHrtf))
{
- const float TargetGain{parms.Hrtf.Target.Gain * (vstate == Playing)};
+ const float TargetGain{parms.Hrtf.Target.Gain * float(vstate == Playing)};
DoHrtfMix(samples, samplesToMix, parms, TargetGain, Counter, OutPos,
(vstate == Playing), Device);
}
@@ -1063,8 +1064,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
/* Update voice positions and buffers as needed. */
DataPosFrac += increment*samplesToMix;
- const uint SrcSamplesDone{DataPosFrac>>MixerFracBits};
- DataPosInt += SrcSamplesDone;
+ DataPosInt += static_cast<int>(DataPosFrac>>MixerFracBits);
DataPosFrac &= MixerFracMask;
uint buffers_done{0u};
@@ -1099,8 +1099,8 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
const uint blocksDone{currentBlock - mCallbackBlockBase};
if(blocksDone < mNumCallbackBlocks)
{
- const size_t byteOffset{blocksDone*mBytesPerBlock};
- const size_t byteEnd{mNumCallbackBlocks*mBytesPerBlock};
+ const size_t byteOffset{blocksDone*size_t{mBytesPerBlock}};
+ const size_t byteEnd{mNumCallbackBlocks*size_t{mBytesPerBlock}};
std::byte *data{BufferListItem->mSamples};
std::copy(data+byteOffset, data+byteEnd, data);
mNumCallbackBlocks -= blocksDone;
@@ -1120,7 +1120,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
if(BufferListItem->mSampleLen > static_cast<uint>(DataPosInt))
break;
- DataPosInt -= BufferListItem->mSampleLen;
+ DataPosInt -= static_cast<int>(BufferListItem->mSampleLen);
++buffers_done;
BufferListItem = BufferListItem->mNext.load(std::memory_order_relaxed);
diff --git a/core/voice.h b/core/voice.h
index a599eda8..f212ebe5 100644
--- a/core/voice.h
+++ b/core/voice.h
@@ -32,7 +32,7 @@ enum class DistanceModel : unsigned char;
using uint = unsigned int;
-#define MAX_SENDS 6
+inline constexpr size_t MaxSendCount{6};
enum class SpatializeMode : unsigned char {
@@ -48,7 +48,7 @@ enum class DirectMode : unsigned char {
};
-constexpr uint MaxPitch{10};
+inline constexpr uint MaxPitch{10};
enum {
@@ -66,14 +66,14 @@ struct DirectParams {
NfcFilter NFCtrlFilter;
struct {
- HrtfFilter Old;
- HrtfFilter Target;
- alignas(16) std::array<float,HrtfHistoryLength> History;
+ HrtfFilter Old{};
+ HrtfFilter Target{};
+ alignas(16) std::array<float,HrtfHistoryLength> History{};
} Hrtf;
struct {
- std::array<float,MAX_OUTPUT_CHANNELS> Current;
- std::array<float,MAX_OUTPUT_CHANNELS> Target;
+ std::array<float,MaxOutputChannels> Current{};
+ std::array<float,MaxOutputChannels> Target{};
} Gains;
};
@@ -82,8 +82,8 @@ struct SendParams {
BiquadFilter HighPass;
struct {
- std::array<float,MaxAmbiChannels> Current;
- std::array<float,MaxAmbiChannels> Target;
+ std::array<float,MaxAmbiChannels> Current{};
+ std::array<float,MaxAmbiChannels> Target{};
} Gains;
};
@@ -154,13 +154,12 @@ struct VoiceProps {
float HFReference;
float GainLF;
float LFReference;
- } Send[MAX_SENDS];
+ };
+ std::array<SendData,MaxSendCount> Send;
};
struct VoicePropsItem : public VoiceProps {
std::atomic<VoicePropsItem*> next{nullptr};
-
- DEF_NEWDEL(VoicePropsItem)
};
enum : uint {
@@ -185,7 +184,7 @@ struct Voice {
std::atomic<VoicePropsItem*> mUpdate{nullptr};
- VoiceProps mProps;
+ VoiceProps mProps{};
std::atomic<uint> mSourceID{0u};
std::atomic<State> mPlayState{Stopped};
@@ -195,30 +194,30 @@ struct Voice {
* Source offset in samples, relative to the currently playing buffer, NOT
* the whole queue.
*/
- std::atomic<int> mPosition;
+ std::atomic<int> mPosition{};
/** Fractional (fixed-point) offset to the next sample. */
- std::atomic<uint> mPositionFrac;
+ std::atomic<uint> mPositionFrac{};
/* Current buffer queue item being played. */
- std::atomic<VoiceBufferItem*> mCurrentBuffer;
+ std::atomic<VoiceBufferItem*> mCurrentBuffer{};
/* Buffer queue item to loop to at end of queue (will be NULL for non-
* looping voices).
*/
- std::atomic<VoiceBufferItem*> mLoopBuffer;
+ std::atomic<VoiceBufferItem*> mLoopBuffer{};
std::chrono::nanoseconds mStartTime{};
/* Properties for the attached buffer(s). */
- FmtChannels mFmtChannels;
- FmtType mFmtType;
- uint mFrequency;
- uint mFrameStep; /**< In steps of the sample type size. */
- uint mBytesPerBlock; /**< Or for PCM formats, BytesPerFrame. */
- uint mSamplesPerBlock; /**< Always 1 for PCM formats. */
- AmbiLayout mAmbiLayout;
- AmbiScaling mAmbiScaling;
- uint mAmbiOrder;
+ FmtChannels mFmtChannels{};
+ FmtType mFmtType{};
+ uint mFrequency{};
+ uint mFrameStep{}; /**< In steps of the sample type size. */
+ uint mBytesPerBlock{}; /**< Or for PCM formats, BytesPerFrame. */
+ uint mSamplesPerBlock{}; /**< Always 1 for PCM formats. */
+ AmbiLayout mAmbiLayout{};
+ AmbiScaling mAmbiScaling{};
+ uint mAmbiOrder{};
std::unique_ptr<DecoderBase> mDecoder;
uint mDecoderPadding{};
@@ -226,20 +225,20 @@ struct Voice {
/** Current target parameters used for mixing. */
uint mStep{0};
- ResamplerFunc mResampler;
+ ResamplerFunc mResampler{};
- InterpState mResampleState;
+ InterpState mResampleState{};
std::bitset<VoiceFlagCount> mFlags{};
uint mNumCallbackBlocks{0};
uint mCallbackBlockBase{0};
struct TargetData {
- int FilterType;
+ int FilterType{};
al::span<FloatBufferLine> Buffer;
};
TargetData mDirect;
- std::array<TargetData,MAX_SENDS> mSend;
+ std::array<TargetData,MaxSendCount> mSend;
/* The first MaxResamplerPadding/2 elements are the sample history from the
* previous mix, with an additional MaxResamplerPadding/2 elements that are
@@ -250,11 +249,11 @@ struct Voice {
al::vector<HistoryLine,16> mPrevSamples{2};
struct ChannelData {
- float mAmbiHFScale, mAmbiLFScale;
+ float mAmbiHFScale{}, mAmbiLFScale{};
BandSplitter mAmbiSplitter;
DirectParams mDryParams;
- std::array<SendParams,MAX_SENDS> mWetParams;
+ std::array<SendParams,MaxSendCount> mWetParams;
};
al::vector<ChannelData> mChans{2};
@@ -270,8 +269,6 @@ struct Voice {
void prepare(DeviceBase *device);
static void InitMixer(std::optional<std::string> resampler);
-
- DEF_NEWDEL(Voice)
};
extern Resampler ResamplerDefault;
diff --git a/core/voice_change.h b/core/voice_change.h
index ddc6186f..e97c48f3 100644
--- a/core/voice_change.h
+++ b/core/voice_change.h
@@ -3,8 +3,6 @@
#include <atomic>
-#include "almalloc.h"
-
struct Voice;
using uint = unsigned int;
@@ -24,8 +22,6 @@ struct VoiceChange {
VChangeState mState{};
std::atomic<VoiceChange*> mNext{nullptr};
-
- DEF_NEWDEL(VoiceChange)
};
#endif /* VOICE_CHANGE_H */