aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/mastering.cpp
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2018-12-20 11:46:57 -0800
committerChris Robinson <[email protected]>2018-12-20 11:46:57 -0800
commit7dc553350bd70b77608b3da175d9146494fb861d (patch)
treeac127cf3f0036a5a58a4a98285dea04cf587b054 /Alc/mastering.cpp
parent08b79b9bbfa96ee6dcbc4d58bf082c9e6df911df (diff)
Clean up most of the compressor loops
Diffstat (limited to 'Alc/mastering.cpp')
-rw-r--r--Alc/mastering.cpp357
1 files changed, 163 insertions, 194 deletions
diff --git a/Alc/mastering.cpp b/Alc/mastering.cpp
index bf999709..007c2657 100644
--- a/Alc/mastering.cpp
+++ b/Alc/mastering.cpp
@@ -1,8 +1,8 @@
#include "config.h"
-#include <math.h>
-
+#include <cmath>
#include <algorithm>
+#include <functional>
#include "mastering.h"
#include "alu.h"
@@ -10,18 +10,6 @@
#include "math_defs.h"
-/* Early MSVC lacks round/roundf */
-#if defined(_MSC_VER) && _MSC_VER < 1800
-static double round(double val)
-{
- if(val < 0.0)
- return ceil(val-0.5);
- return floor(val+0.5);
-}
-#define roundf(f) ((float)round((float)(f)))
-#endif
-
-
/* These structures assume BUFFERSIZE is a power of 2. */
static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
@@ -34,6 +22,10 @@ struct SlidingHold {
};
+namespace {
+
+using namespace std::placeholders;
+
/* This sliding hold follows the input level with an instant attack and a
* fixed duration hold before an instant release to the next highest level.
* It is a sliding window maximum (descending maxima) implementation based on
@@ -43,12 +35,12 @@ struct SlidingHold {
*/
static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
{
- const ALsizei mask = BUFFERSIZE - 1;
- const ALsizei length = Hold->Length;
- ALfloat *RESTRICT values = Hold->Values;
- ALsizei *RESTRICT expiries = Hold->Expiries;
- ALsizei lowerIndex = Hold->LowerIndex;
- ALsizei upperIndex = Hold->UpperIndex;
+ static constexpr ALsizei mask{BUFFERSIZE - 1};
+ const ALsizei length{Hold->Length};
+ ALfloat (&values)[BUFFERSIZE] = Hold->Values;
+ ALsizei (&expiries)[BUFFERSIZE] = Hold->Expiries;
+ ALsizei lowerIndex{Hold->LowerIndex};
+ ALsizei upperIndex{Hold->UpperIndex};
if(i >= expiries[upperIndex])
upperIndex = (upperIndex + 1) & mask;
@@ -83,47 +75,42 @@ static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALflo
static void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
{
- const ALsizei lowerIndex = Hold->LowerIndex;
- ALsizei *RESTRICT expiries = Hold->Expiries;
- ALsizei i = Hold->UpperIndex;
+ ASSUME(Hold->UpperIndex >= 0);
+ ASSUME(Hold->LowerIndex >= 0);
- if(lowerIndex < i)
+ auto exp_begin = std::begin(Hold->Expiries) + Hold->UpperIndex;
+ auto exp_last = std::begin(Hold->Expiries) + Hold->LowerIndex;
+ if(exp_last < exp_begin)
{
- for(;i < BUFFERSIZE;i++)
- expiries[i] -= n;
- i = 0;
+ std::transform(exp_begin, std::end(Hold->Expiries), exp_begin,
+ std::bind(std::minus<float>{}, _1, n));
+ exp_begin = std::begin(Hold->Expiries);
}
- for(;i < lowerIndex;i++)
- expiries[i] -= n;
-
- expiries[i] -= n;
+ std::transform(exp_begin, exp_last+1, exp_begin, std::bind(std::minus<float>{}, _1, n));
}
/* Multichannel compression is linked via the absolute maximum of all
* channels.
*/
-static void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RESTRICT OutBuffer)[BUFFERSIZE])
+void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat (*RESTRICT OutBuffer)[BUFFERSIZE])
{
- const ALsizei index = Comp->LookAhead;
- const ALsizei numChans = Comp->NumChans;
- ALfloat *RESTRICT sideChain = Comp->SideChain;
- ALsizei c, i;
+ const ALsizei index{Comp->LookAhead};
+ const ALsizei numChans{Comp->NumChans};
ASSUME(SamplesToDo > 0);
ASSUME(numChans > 0);
+ ASSUME(index >= 0);
- for(i = 0;i < SamplesToDo;i++)
- sideChain[index + i] = 0.0f;
+ auto side_begin = std::begin(Comp->SideChain) + index;
+ std::fill(side_begin, side_begin+SamplesToDo, 0.0f);
- for(c = 0;c < numChans;c++)
+ auto fill_max = [SamplesToDo,side_begin](const ALfloat *input) -> void
{
- ALsizei offset = index;
- for(i = 0;i < SamplesToDo;i++)
- {
- sideChain[offset] = maxf(sideChain[offset], fabsf(OutBuffer[c][i]));
- ++offset;
- }
- }
+ const ALfloat *RESTRICT buffer{al::assume_aligned<16>(input)};
+ auto max_abs = std::bind(maxf, _1, std::bind(static_cast<float(&)(float)>(std::fabs), _2));
+ std::transform(side_begin, side_begin+SamplesToDo, buffer, side_begin, max_abs);
+ };
+ std::for_each(OutBuffer, OutBuffer+numChans, fill_max);
}
/* This calculates the squared crest factor of the control signal for the
@@ -133,25 +120,24 @@ static void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*
*/
static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
{
- const ALfloat a_crest = Comp->CrestCoeff;
- const ALsizei index = Comp->LookAhead;
- const ALfloat *RESTRICT sideChain = Comp->SideChain;
- ALfloat *RESTRICT crestFactor = Comp->CrestFactor;
- ALfloat y2_peak = Comp->LastPeakSq;
- ALfloat y2_rms = Comp->LastRmsSq;
- ALsizei i;
+ const ALfloat a_crest{Comp->CrestCoeff};
+ const ALsizei index{Comp->LookAhead};
+ ALfloat y2_peak{Comp->LastPeakSq};
+ ALfloat y2_rms{Comp->LastRmsSq};
ASSUME(SamplesToDo > 0);
+ ASSUME(index >= 0);
- for(i = 0;i < SamplesToDo;i++)
+ auto calc_crest = [&y2_rms,&y2_peak,a_crest](const ALfloat x_abs) noexcept -> ALfloat
{
- ALfloat x_abs = sideChain[index + i];
ALfloat x2 = maxf(0.000001f, x_abs * x_abs);
y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest));
y2_rms = lerp(x2, y2_rms, a_crest);
- crestFactor[i] = y2_peak / y2_rms;
- }
+ return y2_peak / y2_rms;
+ };
+ auto side_begin = std::begin(Comp->SideChain) + index;
+ std::transform(side_begin, side_begin+SamplesToDo, std::begin(Comp->CrestFactor), calc_crest);
Comp->LastPeakSq = y2_peak;
Comp->LastRmsSq = y2_rms;
@@ -161,44 +147,39 @@ static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
* value of the incoming signal) and performs most of its operations in the
* log domain.
*/
-static void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
+void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
{
- const ALsizei index = Comp->LookAhead;
- ALfloat *RESTRICT sideChain = Comp->SideChain;
- ALsizei i;
+ const ALsizei index{Comp->LookAhead};
ASSUME(SamplesToDo > 0);
+ ASSUME(index >= 0);
- for(i = 0;i < SamplesToDo;i++)
- {
- const ALuint offset = index + i;
- const ALfloat x_abs = sideChain[offset];
-
- sideChain[offset] = logf(maxf(0.000001f, x_abs));
- }
+ /* Clamp the minimum amplitude to near-zero and convert to logarithm. */
+ auto side_begin = std::begin(Comp->SideChain) + index;
+ std::transform(side_begin, side_begin+SamplesToDo, side_begin,
+ std::bind(static_cast<float(&)(float)>(std::log), std::bind(maxf, 0.000001f, _1)));
}
/* 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.
*/
-static void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
+void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
{
- const ALsizei index = Comp->LookAhead;
- ALfloat *RESTRICT sideChain = Comp->SideChain;
- SlidingHold *hold = Comp->Hold;
- ALsizei i;
+ const ALsizei index{Comp->LookAhead};
ASSUME(SamplesToDo > 0);
+ ASSUME(index >= 0);
- for(i = 0;i < SamplesToDo;i++)
+ SlidingHold *hold{Comp->Hold};
+ ALsizei i{0};
+ auto detect_peak = [&i,hold](const ALfloat x_abs) -> ALfloat
{
- const ALsizei offset = index + i;
- const ALfloat x_abs = sideChain[offset];
- const ALfloat x_G = logf(maxf(0.000001f, x_abs));
-
- sideChain[offset] = UpdateSlidingHold(hold, i, x_G);
- }
+ const ALfloat x_G{std::log(maxf(0.000001f, x_abs))};
+ return UpdateSlidingHold(hold, i++, x_G);
+ };
+ auto side_begin = std::begin(Comp->SideChain) + index;
+ std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak);
ShiftSlidingHold(hold, SamplesToDo);
}
@@ -208,76 +189,68 @@ static void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
* to knee width, attack/release times, make-up/post gain, and clipping
* reduction.
*/
-static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
+void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
{
- const bool autoKnee = Comp->Auto.Knee;
- const bool autoAttack = Comp->Auto.Attack;
- const bool autoRelease = Comp->Auto.Release;
- const bool autoPostGain = Comp->Auto.PostGain;
- const bool autoDeclip = Comp->Auto.Declip;
- const ALsizei lookAhead = Comp->LookAhead;
- const ALfloat threshold = Comp->Threshold;
- const ALfloat slope = Comp->Slope;
- const ALfloat attack = Comp->Attack;
- const ALfloat release = Comp->Release;
- const ALfloat c_est = Comp->GainEstimate;
- const ALfloat a_adp = Comp->AdaptCoeff;
- const ALfloat *RESTRICT crestFactor = Comp->CrestFactor;
- ALfloat *RESTRICT sideChain = Comp->SideChain;
- ALfloat postGain = Comp->PostGain;
- ALfloat knee = Comp->Knee;
- ALfloat t_att = attack;
- ALfloat t_rel = release - attack;
- ALfloat a_att = expf(-1.0f / t_att);
- ALfloat a_rel = expf(-1.0f / t_rel);
- ALfloat y_1 = Comp->LastRelease;
- ALfloat y_L = Comp->LastAttack;
- ALfloat c_dev = Comp->LastGainDev;
- ALsizei i;
+ const bool autoKnee{Comp->Auto.Knee};
+ const bool autoAttack{Comp->Auto.Attack};
+ const bool autoRelease{Comp->Auto.Release};
+ const bool autoPostGain{Comp->Auto.PostGain};
+ const bool autoDeclip{Comp->Auto.Declip};
+ const ALsizei lookAhead{Comp->LookAhead};
+ const ALfloat threshold{Comp->Threshold};
+ const ALfloat slope{Comp->Slope};
+ const ALfloat attack{Comp->Attack};
+ const ALfloat release{Comp->Release};
+ const ALfloat c_est{Comp->GainEstimate};
+ const ALfloat a_adp{Comp->AdaptCoeff};
+ const ALfloat (&crestFactor)[BUFFERSIZE] = Comp->CrestFactor;
+ ALfloat (&sideChain)[BUFFERSIZE*2] = Comp->SideChain;
+ ALfloat postGain{Comp->PostGain};
+ ALfloat knee{Comp->Knee};
+ ALfloat t_att{attack};
+ ALfloat t_rel{release - attack};
+ ALfloat a_att{std::exp(-1.0f / t_att)};
+ ALfloat a_rel{std::exp(-1.0f / t_rel)};
+ ALfloat y_1{Comp->LastRelease};
+ ALfloat y_L{Comp->LastAttack};
+ ALfloat c_dev{Comp->LastGainDev};
ASSUME(SamplesToDo > 0);
+ ASSUME(lookAhead >= 0);
- for(i = 0;i < SamplesToDo;i++)
+ for(ALsizei i{0};i < SamplesToDo;i++)
{
- const ALfloat y2_crest = crestFactor[i];
- const ALfloat x_G = sideChain[lookAhead + i];
- const ALfloat x_over = x_G - threshold;
- ALfloat knee_h;
- ALfloat y_G;
- ALfloat x_L;
-
if(autoKnee)
knee = maxf(0.0f, 2.5f * (c_dev + c_est));
- knee_h = 0.5f * knee;
+ const ALfloat knee_h{0.5f * knee};
/* This is the gain computer. It applies a static compression curve
* to the control signal.
*/
- if(x_over <= -knee_h)
- y_G = 0.0f;
- else if(fabsf(x_over) < knee_h)
- y_G = (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee);
- else
- y_G = x_over;
-
- x_L = -slope * y_G;
-
+ const ALfloat x_over{sideChain[lookAhead+i] - threshold};
+ const ALfloat 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) :
+ x_over
+ };
+
+ const ALfloat y2_crest{crestFactor[i]};
if(autoAttack)
{
- t_att = 2.0f * attack / y2_crest;
- a_att = expf(-1.0f / t_att);
+ t_att = 2.0f*attack/y2_crest;
+ a_att = std::exp(-1.0f / t_att);
}
-
if(autoRelease)
{
- t_rel = 2.0f * release / y2_crest - t_att;
- a_rel = expf(-1.0f / t_rel);
+ t_rel = 2.0f*release/y2_crest - t_att;
+ a_rel = std::exp(-1.0f / t_rel);
}
/* Gain smoothing (ballistics) is done via a smooth decoupled peak
* detector. The attack time is subtracted from the release time
* above to compensate for the chained operating mode.
*/
+ const ALfloat x_L{-slope * y_G};
y_1 = maxf(x_L, lerp(x_L, y_1, a_rel));
y_L = lerp(y_1, y_L, a_att);
@@ -286,15 +259,15 @@ static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
* The estimate is also used to bias the measurement to hot-start its
* average.
*/
- c_dev = lerp(-y_L - c_est, c_dev, a_adp);
+ c_dev = lerp(-(y_L+c_est), c_dev, a_adp);
if(autoPostGain)
{
/* Clipping reduction is only viable when make-up gain is being
- * automated. It modifies the deviation to further attenuate the
- * control signal when clipping is detected. The adaptation
- * time is sufficiently long enough to suppress further clipping
- * at the same output level.
+ * automated. It modifies the deviation to further attenuate the
+ * control signal when clipping is detected. The adaptation time
+ * is sufficiently long enough to suppress further clipping at the
+ * same output level.
*/
if(autoDeclip)
c_dev = maxf(c_dev, sideChain[i] - y_L - threshold - c_est);
@@ -302,7 +275,7 @@ static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
postGain = -(c_dev + c_est);
}
- sideChain[i] = expf(postGain - y_L);
+ sideChain[i] = std::exp(postGain - y_L);
}
Comp->LastRelease = y_1;
@@ -315,32 +288,34 @@ static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
* reaching the offending impulse. This is best used when operating as a
* limiter.
*/
-static void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RESTRICT OutBuffer)[BUFFERSIZE])
+void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RESTRICT OutBuffer)[BUFFERSIZE])
{
- const ALsizei mask = BUFFERSIZE - 1;
- const ALsizei numChans = Comp->NumChans;
- const ALsizei indexIn = Comp->DelayIndex;
- const ALsizei indexOut = Comp->DelayIndex - Comp->LookAhead;
- ALfloat (*RESTRICT delay)[BUFFERSIZE] = Comp->Delay;
- ALsizei c, i;
+ static constexpr ALsizei mask{BUFFERSIZE - 1};
+ const ALsizei numChans{Comp->NumChans};
+ const ALsizei indexIn{Comp->DelayIndex};
+ const ALsizei indexOut{Comp->DelayIndex - Comp->LookAhead};
ASSUME(SamplesToDo > 0);
ASSUME(numChans > 0);
- for(c = 0;c < numChans;c++)
+ for(ALsizei c{0};c < numChans;c++)
{
- for(i = 0;i < SamplesToDo;i++)
+ ALfloat *RESTRICT inout{al::assume_aligned<16>(OutBuffer[c])};
+ ALfloat *RESTRICT delay{al::assume_aligned<16>(Comp->Delay[c])};
+ for(ALsizei i{0};i < SamplesToDo;i++)
{
- ALfloat sig = OutBuffer[c][i];
+ const ALfloat sig{inout[i]};
- OutBuffer[c][i] = delay[c][(indexOut + i) & mask];
- delay[c][(indexIn + i) & mask] = sig;
+ inout[i] = delay[(indexOut + i) & mask];
+ delay[(indexIn + i) & mask] = sig;
}
}
Comp->DelayIndex = (indexIn + SamplesToDo) & mask;
}
+} // namespace
+
/* The compressor is initialized with the following settings:
*
* NumChans - Number of channels to process.
@@ -374,25 +349,20 @@ Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
const ALfloat Ratio, const ALfloat KneeDb,
const ALfloat AttackTime, const ALfloat ReleaseTime)
{
- Compressor *Comp;
- ALsizei lookAhead;
- ALsizei hold;
- size_t size;
-
- lookAhead = (ALsizei)clampf(roundf(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1);
- hold = (ALsizei)clampf(roundf(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1);
- /* 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)
- hold = 0;
+ auto lookAhead = static_cast<ALsizei>(
+ clampf(std::round(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1));
+ auto hold = static_cast<ALsizei>(clampf(std::round(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1));
- size = sizeof(*Comp);
+ Compressor *Comp;
+ size_t size{sizeof(*Comp)};
if(lookAhead > 0)
{
size += sizeof(*Comp->Delay) * NumChans;
- if(hold > 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)
size += sizeof(*Comp->Hold);
}
@@ -405,11 +375,11 @@ Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
Comp->Auto.PostGain = AutoPostGain;
Comp->Auto.Declip = AutoPostGain && AutoDeclip;
Comp->LookAhead = lookAhead;
- Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
- Comp->PostGain = PostGainDb * logf(10.0f) / 20.0f;
- Comp->Threshold = ThresholdDb * logf(10.0f) / 20.0f;
+ Comp->PreGain = std::pow(10.0f, PreGainDb / 20.0f);
+ Comp->PostGain = PostGainDb * std::log(10.0f) / 20.0f;
+ Comp->Threshold = ThresholdDb * std::log(10.0f) / 20.0f;
Comp->Slope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
- Comp->Knee = maxf(0.0f, KneeDb * logf(10.0f) / 20.0f);
+ Comp->Knee = maxf(0.0f, KneeDb * std::log(10.0f) / 20.0f);
Comp->Attack = maxf(1.0f, AttackTime * SampleRate);
Comp->Release = maxf(1.0f, ReleaseTime * SampleRate);
@@ -422,7 +392,7 @@ Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
if(lookAhead > 0)
{
- if(hold > 0)
+ if(hold > 1)
{
Comp->Hold = new ((void*)(Comp + 1)) SlidingHold{};
Comp->Hold->Values[0] = -HUGE_VALF;
@@ -436,14 +406,14 @@ Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
}
}
- Comp->CrestCoeff = expf(-1.0f / (0.200f * SampleRate)); // 200ms
+ Comp->CrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms
Comp->GainEstimate = Comp->Threshold * -0.5f * Comp->Slope;
- Comp->AdaptCoeff = expf(-1.0f / (2.0f * SampleRate)); // 2s
+ Comp->AdaptCoeff = std::exp(-1.0f / (2.0f * SampleRate)); // 2s
return Comp;
}
-void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RESTRICT OutBuffer)[BUFFERSIZE])
+void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*OutBuffer)[BUFFERSIZE])
{
const ALsizei numChans{Comp->NumChans};
@@ -453,15 +423,13 @@ void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RES
const ALfloat preGain{Comp->PreGain};
if(preGain != 1.0f)
{
- std::for_each(OutBuffer, OutBuffer+numChans,
- [SamplesToDo, preGain](ALfloat *buffer) noexcept -> void
- {
- std::for_each(buffer, buffer+SamplesToDo,
- [preGain](ALfloat &samp) noexcept -> void
- { samp *= preGain; }
- );
- }
- );
+ auto apply_gain = [SamplesToDo,preGain](ALfloat *input) noexcept -> void
+ {
+ ALfloat *buffer{al::assume_aligned<16>(input)};
+ std::transform(buffer, buffer+SamplesToDo, buffer,
+ std::bind(std::multiplies<float>{}, _1, preGain));
+ };
+ std::for_each(OutBuffer, OutBuffer+numChans, apply_gain);
}
LinkChannels(Comp, SamplesToDo, OutBuffer);
@@ -479,22 +447,23 @@ void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RES
if(Comp->Delay)
SignalDelay(Comp, SamplesToDo, OutBuffer);
- ALfloat *RESTRICT sideChain{Comp->SideChain};
- std::for_each(OutBuffer, OutBuffer+numChans,
- [SamplesToDo, sideChain](ALfloat *buffer) noexcept -> void
- {
- /* Mark the sideChain "input-1 type" as restrict, so the compiler
- * can vectorize this loop (otherwise it assumes a write to
- * buffer[n] can change sideChain[n+1]).
- */
- std::transform<ALfloat*RESTRICT>(sideChain, sideChain+SamplesToDo, buffer, buffer,
- [](const ALfloat gain, const ALfloat samp) noexcept -> ALfloat
- { return samp * gain; }
- );
- }
- );
-
- std::copy(sideChain+SamplesToDo, sideChain+SamplesToDo+Comp->LookAhead, sideChain);
+ const ALfloat (&sideChain)[BUFFERSIZE*2] = Comp->SideChain;
+ auto apply_comp = [SamplesToDo,&sideChain](ALfloat *input) noexcept -> void
+ {
+ ALfloat *buffer{al::assume_aligned<16>(input)};
+ const ALfloat *gains{al::assume_aligned<16>(sideChain)};
+ /* Mark the gains "input-1 type" as restrict, so the compiler can
+ * vectorize this loop (otherwise it assumes a write to buffer[n] can
+ * change gains[n+1]).
+ */
+ std::transform<const ALfloat*RESTRICT>(gains, gains+SamplesToDo, buffer, buffer,
+ std::bind(std::multiplies<float>{}, _1, _2));
+ };
+ std::for_each(OutBuffer, OutBuffer+numChans, apply_comp);
+
+ ASSUME(Comp->LookAhead >= 0);
+ auto side_begin = std::begin(Comp->SideChain) + SamplesToDo;
+ std::copy(side_begin, side_begin+Comp->LookAhead, std::begin(Comp->SideChain));
}