diff options
author | Chris Robinson <[email protected]> | 2023-12-31 07:58:57 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2023-12-31 07:58:57 -0800 |
commit | 77a571e42ada313ed11d37a3738214df57d378ef (patch) | |
tree | 395473c33156ceaba39fbe02ad7f0f807d279ebe /core | |
parent | ba4f633a9e778b3d94f2e21d124cad2baf05a6b4 (diff) |
Avoid placement new for the output limiter
Diffstat (limited to 'core')
-rw-r--r-- | core/device.h | 2 | ||||
-rw-r--r-- | core/mastering.cpp | 140 | ||||
-rw-r--r-- | core/mastering.h | 18 |
3 files changed, 72 insertions, 88 deletions
diff --git a/core/device.h b/core/device.h index f02700c6..9ee07454 100644 --- a/core/device.h +++ b/core/device.h @@ -29,7 +29,7 @@ class BFormatDec; namespace Bs2b { struct bs2b; } // namespace Bs2b -struct Compressor; +class Compressor; struct ContextBase; struct DirectHrtfState; struct HrtfStore; diff --git a/core/mastering.cpp b/core/mastering.cpp index 9bb4c053..9eaabc35 100644 --- a/core/mastering.cpp +++ b/core/mastering.cpp @@ -96,18 +96,16 @@ void ShiftSlidingHold(SlidingHold *Hold, const uint n) [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); - const auto side_begin = Comp->mSideChain.begin() + 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 @@ -117,7 +115,7 @@ void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLin { 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 @@ -125,11 +123,11 @@ 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); @@ -141,23 +139,23 @@ void CrestDetector(Compressor *Comp, const uint SamplesToDo) y2_rms = lerpf(x2, y2_rms, a_crest); return y2_peak / y2_rms; }; - const auto side_begin = Comp->mSideChain.begin() + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, Comp->mCrestFactor.begin(), 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 logarithmic. */ - const auto side_begin = Comp->mSideChain.begin() + Comp->mLookAhead; + const auto side_begin = mSideChain.begin() + mLookAhead; std::transform(side_begin, side_begin+SamplesToDo, side_begin, [](float s) { return std::log(std::max(0.000001f, s)); }); } @@ -166,18 +164,18 @@ void PeakDetector(Compressor *Comp, const uint SamplesToDo) * 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(std::max(0.000001f, x_abs))}; return UpdateSlidingHold(hold, i++, x_G); }; - auto side_begin = Comp->mSideChain.begin() + Comp->mLookAhead; + auto side_begin = mSideChain.begin() + mLookAhead; std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak); ShiftSlidingHold(hold, SamplesToDo); @@ -188,30 +186,30 @@ 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 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}; - auto lookAhead = Comp->mSideChain.cbegin() + Comp->mLookAhead; - auto crestFactor = Comp->mCrestFactor.cbegin(); - 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); @@ -273,12 +271,12 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) return std::exp(postGain - y_L); }; - auto sideChain = al::span{Comp->mSideChain}.first(SamplesToDo); + 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 @@ -286,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); @@ -298,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 @@ -314,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, @@ -328,22 +324,7 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa 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[0]) * 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); - } - - gsl::owner<void*> storage{al_calloc(16, size)}; - if(!storage) throw std::bad_alloc{}; - - auto Comp = CompressorPtr{::new(storage) Compressor{}}; + auto Comp = CompressorPtr{new Compressor{}}; Comp->mNumChans = NumChans; Comp->mAuto.Knee = AutoKnee; Comp->mAuto.Attack = AutoAttack; @@ -368,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), NumChans}; } - else - Comp->mDelay = {reinterpret_cast<FloatBufferLine*>(Comp.get() + 1), NumChans}; - std::uninitialized_fill(Comp->mDelay.begin(), Comp->mDelay.end(), FloatBufferLine{}); + Comp->mDelay.resize(NumChans, FloatBufferLine{}); } Comp->mCrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms @@ -388,13 +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; - std::destroy(mDelay.begin(), mDelay.end()); -} +Compressor::~Compressor() = default; void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer) @@ -416,20 +392,20 @@ void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer) 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.empty()) - SignalDelay(this, SamplesToDo, OutBuffer); + signalDelay(SamplesToDo, OutBuffer); const auto sideChain = al::span{mSideChain}; auto apply_comp = [SamplesToDo,sideChain](FloatBufferLine &input) noexcept -> void diff --git a/core/mastering.h b/core/mastering.h index 4a3b59cf..5fb7133e 100644 --- a/core/mastering.h +++ b/core/mastering.h @@ -8,6 +8,7 @@ #include "alnumeric.h" #include "alspan.h" #include "bufferline.h" +#include "vector.h" struct SlidingHold; @@ -24,7 +25,7 @@ using uint = unsigned int; * * http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/ */ -struct Compressor { +class Compressor { size_t mNumChans{0u}; struct { @@ -50,8 +51,8 @@ struct Compressor { alignas(16) std::array<float,BufferLineSize*2_uz> mSideChain{}; alignas(16) std::array<float,BufferLineSize> mCrestFactor{}; - SlidingHold *mHold{nullptr}; - al::span<FloatBufferLine> mDelay; + std::unique_ptr<SlidingHold> mHold; + al::vector<FloatBufferLine> mDelay; float mCrestCoeff{0.0f}; float mGainEstimate{0.0f}; @@ -63,13 +64,20 @@ 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); [[nodiscard]] auto getLookAhead() const noexcept -> uint { return mLookAhead; } - DEF_PLACE_NEWDEL - /** * The compressor is initialized with the following settings: * |