diff options
Diffstat (limited to 'core')
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 */ |