Make the Compressor more class-like
This commit is contained in:
+5
-6
@@ -1550,7 +1550,7 @@ static void alcSetError(ALCdevice *device, ALCenum errorCode)
|
||||
}
|
||||
|
||||
|
||||
static Compressor *CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
|
||||
static std::unique_ptr<Compressor> CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
|
||||
{
|
||||
return CompressorInit(device->RealOut.NumChannels, device->Frequency,
|
||||
AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, 0.001f, 0.002f,
|
||||
@@ -1816,6 +1816,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
device->Uhj_Encoder = nullptr;
|
||||
device->Bs2b = nullptr;
|
||||
|
||||
device->Limiter = nullptr;
|
||||
device->ChannelDelay.clear();
|
||||
device->ChannelDelay.shrink_to_fit();
|
||||
|
||||
@@ -2044,14 +2045,12 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
if(device->DitherDepth > 0.0f)
|
||||
thrshld -= 1.0f / device->DitherDepth;
|
||||
|
||||
device->Limiter.reset(CreateDeviceLimiter(device, std::log10(thrshld) * 20.0f));
|
||||
auto limiter = CreateDeviceLimiter(device, std::log10(thrshld) * 20.0f);
|
||||
/* Convert the lookahead from samples to nanosamples to nanoseconds. */
|
||||
device->FixedLatency += std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::seconds(GetCompressorLookAhead(device->Limiter.get()))
|
||||
) / device->Frequency;
|
||||
std::chrono::seconds(limiter->getLookAhead())) / device->Frequency;
|
||||
device->Limiter = std::move(limiter);
|
||||
}
|
||||
else
|
||||
device->Limiter = nullptr;
|
||||
TRACE("Output limiter %s\n", device->Limiter ? "enabled" : "disabled");
|
||||
|
||||
aluSelectPostProcess(device);
|
||||
|
||||
+2
-2
@@ -1745,8 +1745,8 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
||||
}
|
||||
|
||||
/* Apply compression, limiting sample amplitude if needed or desired. */
|
||||
if(device->Limiter)
|
||||
ApplyCompression(device->Limiter.get(), SamplesToDo, device->RealOut.Buffer);
|
||||
if(Compressor *comp{device->Limiter.get()})
|
||||
comp->process(SamplesToDo, device->RealOut.Buffer);
|
||||
|
||||
/* Apply delays and attenuation for mismatched speaker distances. */
|
||||
ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
|
||||
|
||||
+123
-115
@@ -15,11 +15,11 @@
|
||||
static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
|
||||
|
||||
struct SlidingHold {
|
||||
ALfloat Values[BUFFERSIZE];
|
||||
ALsizei Expiries[BUFFERSIZE];
|
||||
ALsizei LowerIndex;
|
||||
ALsizei UpperIndex;
|
||||
ALsizei Length;
|
||||
ALfloat mValues[BUFFERSIZE];
|
||||
ALsizei mExpiries[BUFFERSIZE];
|
||||
ALsizei mLowerIndex;
|
||||
ALsizei mUpperIndex;
|
||||
ALsizei mLength;
|
||||
};
|
||||
|
||||
|
||||
@@ -34,14 +34,17 @@ using namespace std::placeholders;
|
||||
*
|
||||
* http://www.richardhartersworld.com/cri/2001/slidingmin.html
|
||||
*/
|
||||
static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
|
||||
ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
|
||||
{
|
||||
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};
|
||||
const ALsizei length{Hold->mLength};
|
||||
ALfloat (&values)[BUFFERSIZE] = Hold->mValues;
|
||||
ALsizei (&expiries)[BUFFERSIZE] = Hold->mExpiries;
|
||||
ALsizei lowerIndex{Hold->mLowerIndex};
|
||||
ALsizei upperIndex{Hold->mUpperIndex};
|
||||
|
||||
ASSUME(upperIndex >= 0);
|
||||
ASSUME(lowerIndex >= 0);
|
||||
|
||||
if(i >= expiries[upperIndex])
|
||||
upperIndex = (upperIndex + 1) & mask;
|
||||
@@ -68,41 +71,42 @@ static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALflo
|
||||
expiries[lowerIndex] = i + length;
|
||||
}
|
||||
|
||||
Hold->LowerIndex = lowerIndex;
|
||||
Hold->UpperIndex = upperIndex;
|
||||
Hold->mLowerIndex = lowerIndex;
|
||||
Hold->mUpperIndex = upperIndex;
|
||||
|
||||
return values[upperIndex];
|
||||
}
|
||||
|
||||
static void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
|
||||
void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
|
||||
{
|
||||
ASSUME(Hold->UpperIndex >= 0);
|
||||
ASSUME(Hold->LowerIndex >= 0);
|
||||
ASSUME(Hold->mUpperIndex >= 0);
|
||||
ASSUME(Hold->mLowerIndex >= 0);
|
||||
|
||||
auto exp_begin = std::begin(Hold->Expiries) + Hold->UpperIndex;
|
||||
auto exp_last = std::begin(Hold->Expiries) + Hold->LowerIndex;
|
||||
auto exp_begin = std::begin(Hold->mExpiries) + Hold->mUpperIndex;
|
||||
auto exp_last = std::begin(Hold->mExpiries) + Hold->mLowerIndex;
|
||||
if(exp_last < exp_begin)
|
||||
{
|
||||
std::transform(exp_begin, std::end(Hold->Expiries), exp_begin,
|
||||
std::transform(exp_begin, std::end(Hold->mExpiries), exp_begin,
|
||||
std::bind(std::minus<ALsizei>{}, _1, n));
|
||||
exp_begin = std::begin(Hold->Expiries);
|
||||
exp_begin = std::begin(Hold->mExpiries);
|
||||
}
|
||||
std::transform(exp_begin, exp_last+1, exp_begin, std::bind(std::minus<ALsizei>{}, _1, n));
|
||||
}
|
||||
|
||||
|
||||
/* Multichannel compression is linked via the absolute maximum of all
|
||||
* channels.
|
||||
*/
|
||||
void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat (*RESTRICT OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei index{Comp->LookAhead};
|
||||
const ALsizei numChans{Comp->NumChans};
|
||||
const ALsizei index{Comp->mLookAhead};
|
||||
const ALsizei numChans{Comp->mNumChans};
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
ASSUME(index >= 0);
|
||||
|
||||
auto side_begin = std::begin(Comp->SideChain) + index;
|
||||
auto side_begin = std::begin(Comp->mSideChain) + index;
|
||||
std::fill(side_begin, side_begin+SamplesToDo, 0.0f);
|
||||
|
||||
auto fill_max = [SamplesToDo,side_begin](const ALfloat *input) -> void
|
||||
@@ -121,10 +125,10 @@ void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat (*R
|
||||
*/
|
||||
static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALfloat a_crest{Comp->CrestCoeff};
|
||||
const ALsizei index{Comp->LookAhead};
|
||||
ALfloat y2_peak{Comp->LastPeakSq};
|
||||
ALfloat y2_rms{Comp->LastRmsSq};
|
||||
const ALfloat a_crest{Comp->mCrestCoeff};
|
||||
const ALsizei index{Comp->mLookAhead};
|
||||
ALfloat y2_peak{Comp->mLastPeakSq};
|
||||
ALfloat y2_rms{Comp->mLastRmsSq};
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(index >= 0);
|
||||
@@ -137,11 +141,11 @@ static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
y2_rms = lerp(x2, y2_rms, a_crest);
|
||||
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);
|
||||
auto side_begin = std::begin(Comp->mSideChain) + index;
|
||||
std::transform(side_begin, side_begin+SamplesToDo, std::begin(Comp->mCrestFactor), calc_crest);
|
||||
|
||||
Comp->LastPeakSq = y2_peak;
|
||||
Comp->LastRmsSq = y2_rms;
|
||||
Comp->mLastPeakSq = y2_peak;
|
||||
Comp->mLastRmsSq = y2_rms;
|
||||
}
|
||||
|
||||
/* The side-chain starts with a simple peak detector (based on the absolute
|
||||
@@ -150,13 +154,13 @@ static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
*/
|
||||
void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALsizei index{Comp->LookAhead};
|
||||
const ALsizei index{Comp->mLookAhead};
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(index >= 0);
|
||||
|
||||
/* Clamp the minimum amplitude to near-zero and convert to logarithm. */
|
||||
auto side_begin = std::begin(Comp->SideChain) + index;
|
||||
auto side_begin = std::begin(Comp->mSideChain) + index;
|
||||
std::transform(side_begin, side_begin+SamplesToDo, side_begin,
|
||||
std::bind(static_cast<float(&)(float)>(std::log), std::bind(maxf, 0.000001f, _1)));
|
||||
}
|
||||
@@ -167,19 +171,19 @@ void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
*/
|
||||
void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALsizei index{Comp->LookAhead};
|
||||
const ALsizei index{Comp->mLookAhead};
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(index >= 0);
|
||||
|
||||
SlidingHold *hold{Comp->Hold};
|
||||
SlidingHold *hold{Comp->mHold};
|
||||
ALsizei i{0};
|
||||
auto detect_peak = [&i,hold](const ALfloat x_abs) -> ALfloat
|
||||
{
|
||||
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;
|
||||
auto side_begin = std::begin(Comp->mSideChain) + index;
|
||||
std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak);
|
||||
|
||||
ShiftSlidingHold(hold, SamplesToDo);
|
||||
@@ -192,29 +196,29 @@ void PeakHoldDetector(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 (&crestFactor)[BUFFERSIZE] = Comp->CrestFactor;
|
||||
ALfloat (&sideChain)[BUFFERSIZE*2] = Comp->SideChain;
|
||||
ALfloat postGain{Comp->PostGain};
|
||||
ALfloat knee{Comp->Knee};
|
||||
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 ALsizei lookAhead{Comp->mLookAhead};
|
||||
const ALfloat threshold{Comp->mThreshold};
|
||||
const ALfloat slope{Comp->mSlope};
|
||||
const ALfloat attack{Comp->mAttack};
|
||||
const ALfloat release{Comp->mRelease};
|
||||
const ALfloat c_est{Comp->mGainEstimate};
|
||||
const ALfloat a_adp{Comp->mAdaptCoeff};
|
||||
const ALfloat (&crestFactor)[BUFFERSIZE] = Comp->mCrestFactor;
|
||||
ALfloat (&sideChain)[BUFFERSIZE*2] = Comp->mSideChain;
|
||||
ALfloat postGain{Comp->mPostGain};
|
||||
ALfloat knee{Comp->mKnee};
|
||||
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};
|
||||
ALfloat y_1{Comp->mLastRelease};
|
||||
ALfloat y_L{Comp->mLastAttack};
|
||||
ALfloat c_dev{Comp->mLastGainDev};
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(lookAhead >= 0);
|
||||
@@ -279,9 +283,9 @@ void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
sideChain[i] = std::exp(postGain - y_L);
|
||||
}
|
||||
|
||||
Comp->LastRelease = y_1;
|
||||
Comp->LastAttack = y_L;
|
||||
Comp->LastGainDev = c_dev;
|
||||
Comp->mLastRelease = y_1;
|
||||
Comp->mLastAttack = y_L;
|
||||
Comp->mLastGainDev = c_dev;
|
||||
}
|
||||
|
||||
/* Combined with the hold time, a look-ahead delay can improve handling of
|
||||
@@ -292,9 +296,9 @@ void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RESTRICT OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
static constexpr ALsizei mask{BUFFERSIZE - 1};
|
||||
const ALsizei numChans{Comp->NumChans};
|
||||
const ALsizei indexIn{Comp->DelayIndex};
|
||||
const ALsizei indexOut{Comp->DelayIndex - Comp->LookAhead};
|
||||
const ALsizei numChans{Comp->mNumChans};
|
||||
const ALsizei indexIn{Comp->mDelayIndex};
|
||||
const ALsizei indexOut{Comp->mDelayIndex - Comp->mLookAhead};
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
@@ -302,7 +306,7 @@ void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RESTRICT
|
||||
for(ALsizei c{0};c < numChans;c++)
|
||||
{
|
||||
ALfloat *RESTRICT inout{al::assume_aligned<16>(OutBuffer[c])};
|
||||
ALfloat *RESTRICT delay{al::assume_aligned<16>(Comp->Delay[c])};
|
||||
ALfloat *RESTRICT delay{al::assume_aligned<16>(Comp->mDelay[c])};
|
||||
for(ALsizei i{0};i < SamplesToDo;i++)
|
||||
{
|
||||
const ALfloat sig{inout[i]};
|
||||
@@ -312,7 +316,7 @@ void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RESTRICT
|
||||
}
|
||||
}
|
||||
|
||||
Comp->DelayIndex = (indexIn + SamplesToDo) & mask;
|
||||
Comp->mDelayIndex = (indexIn + SamplesToDo) & mask;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -341,7 +345,7 @@ void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*RESTRICT
|
||||
* ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
|
||||
* automating release time.
|
||||
*/
|
||||
Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
std::unique_ptr<Compressor> CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
const ALboolean AutoKnee, const ALboolean AutoAttack,
|
||||
const ALboolean AutoRelease, const ALboolean AutoPostGain,
|
||||
const ALboolean AutoDeclip, const ALfloat LookAheadTime,
|
||||
@@ -354,74 +358,82 @@ Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
clampf(std::round(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1));
|
||||
auto hold = static_cast<ALsizei>(clampf(std::round(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1));
|
||||
|
||||
Compressor *Comp;
|
||||
size_t size{sizeof(*Comp)};
|
||||
std::unique_ptr<Compressor> Comp;
|
||||
size_t size{sizeof(Compressor)};
|
||||
if(lookAhead > 0)
|
||||
{
|
||||
size += sizeof(*Comp->Delay) * NumChans;
|
||||
size += sizeof(*Comp->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(*Comp->Hold);
|
||||
size += sizeof(*Comp->mHold);
|
||||
}
|
||||
|
||||
Comp = new (al_calloc(16, size)) Compressor{};
|
||||
Comp->NumChans = NumChans;
|
||||
Comp->SampleRate = SampleRate;
|
||||
Comp->Auto.Knee = AutoKnee != AL_FALSE;
|
||||
Comp->Auto.Attack = AutoAttack != AL_FALSE;
|
||||
Comp->Auto.Release = AutoRelease != AL_FALSE;
|
||||
Comp->Auto.PostGain = AutoPostGain != AL_FALSE;
|
||||
Comp->Auto.Declip = AutoPostGain && AutoDeclip;
|
||||
Comp->LookAhead = lookAhead;
|
||||
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 * std::log(10.0f) / 20.0f);
|
||||
Comp->Attack = maxf(1.0f, AttackTime * SampleRate);
|
||||
Comp->Release = maxf(1.0f, ReleaseTime * SampleRate);
|
||||
Comp = std::unique_ptr<Compressor>{new (al_calloc(16, size)) Compressor{}};
|
||||
Comp->mNumChans = NumChans;
|
||||
Comp->mSampleRate = SampleRate;
|
||||
Comp->mAuto.Knee = AutoKnee != AL_FALSE;
|
||||
Comp->mAuto.Attack = AutoAttack != AL_FALSE;
|
||||
Comp->mAuto.Release = AutoRelease != AL_FALSE;
|
||||
Comp->mAuto.PostGain = AutoPostGain != AL_FALSE;
|
||||
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);
|
||||
|
||||
/* Knee width automation actually treats the compressor as a limiter. By
|
||||
* varying the knee width, it can effectively be seen as applying
|
||||
* compression over a wide range of ratios.
|
||||
*/
|
||||
if(AutoKnee)
|
||||
Comp->Slope = -1.0f;
|
||||
Comp->mSlope = -1.0f;
|
||||
|
||||
if(lookAhead > 0)
|
||||
{
|
||||
if(hold > 1)
|
||||
{
|
||||
Comp->Hold = new ((void*)(Comp + 1)) SlidingHold{};
|
||||
Comp->Hold->Values[0] = -std::numeric_limits<float>::infinity();
|
||||
Comp->Hold->Expiries[0] = hold;
|
||||
Comp->Hold->Length = hold;
|
||||
Comp->Delay = (ALfloat(*)[BUFFERSIZE])(Comp->Hold + 1);
|
||||
Comp->mHold = new ((void*)(Comp.get() + 1)) SlidingHold{};
|
||||
Comp->mHold->mValues[0] = -std::numeric_limits<float>::infinity();
|
||||
Comp->mHold->mExpiries[0] = hold;
|
||||
Comp->mHold->mLength = hold;
|
||||
Comp->mDelay = (ALfloat(*)[BUFFERSIZE])(Comp->mHold + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Comp->Delay = (ALfloat(*)[BUFFERSIZE])(Comp + 1);
|
||||
Comp->mDelay = (ALfloat(*)[BUFFERSIZE])(Comp.get() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Comp->CrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms
|
||||
Comp->GainEstimate = Comp->Threshold * -0.5f * Comp->Slope;
|
||||
Comp->AdaptCoeff = std::exp(-1.0f / (2.0f * SampleRate)); // 2s
|
||||
Comp->mCrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms
|
||||
Comp->mGainEstimate = Comp->mThreshold * -0.5f * Comp->mSlope;
|
||||
Comp->mAdaptCoeff = std::exp(-1.0f / (2.0f * SampleRate)); // 2s
|
||||
|
||||
return Comp;
|
||||
}
|
||||
|
||||
void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*OutBuffer)[BUFFERSIZE])
|
||||
Compressor::~Compressor()
|
||||
{
|
||||
const ALsizei numChans{Comp->NumChans};
|
||||
if(mHold)
|
||||
mHold->~SlidingHold();
|
||||
mHold = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void Compressor::process(const ALsizei SamplesToDo, ALfloat (*OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei numChans{mNumChans};
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
const ALfloat preGain{Comp->PreGain};
|
||||
const ALfloat preGain{mPreGain};
|
||||
if(preGain != 1.0f)
|
||||
{
|
||||
auto apply_gain = [SamplesToDo,preGain](ALfloat *input) noexcept -> void
|
||||
@@ -433,26 +445,26 @@ void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*Out
|
||||
std::for_each(OutBuffer, OutBuffer+numChans, apply_gain);
|
||||
}
|
||||
|
||||
LinkChannels(Comp, SamplesToDo, OutBuffer);
|
||||
LinkChannels(this, SamplesToDo, OutBuffer);
|
||||
|
||||
if(Comp->Auto.Attack || Comp->Auto.Release)
|
||||
CrestDetector(Comp, SamplesToDo);
|
||||
if(mAuto.Attack || mAuto.Release)
|
||||
CrestDetector(this, SamplesToDo);
|
||||
|
||||
if(Comp->Hold)
|
||||
PeakHoldDetector(Comp, SamplesToDo);
|
||||
if(mHold)
|
||||
PeakHoldDetector(this, SamplesToDo);
|
||||
else
|
||||
PeakDetector(Comp, SamplesToDo);
|
||||
PeakDetector(this, SamplesToDo);
|
||||
|
||||
GainCompressor(Comp, SamplesToDo);
|
||||
GainCompressor(this, SamplesToDo);
|
||||
|
||||
if(Comp->Delay)
|
||||
SignalDelay(Comp, SamplesToDo, OutBuffer);
|
||||
if(mDelay)
|
||||
SignalDelay(this, SamplesToDo, OutBuffer);
|
||||
|
||||
const ALfloat (&sideChain)[BUFFERSIZE*2] = Comp->SideChain;
|
||||
const ALfloat (&sideChain)[BUFFERSIZE*2] = mSideChain;
|
||||
auto apply_comp = [SamplesToDo,&sideChain](ALfloat *input) noexcept -> void
|
||||
{
|
||||
ALfloat *buffer{al::assume_aligned<16>(input)};
|
||||
const ALfloat *gains{al::assume_aligned<16>(sideChain)};
|
||||
const ALfloat *gains{al::assume_aligned<16>(&sideChain[0])};
|
||||
/* 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]).
|
||||
@@ -462,11 +474,7 @@ void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*Out
|
||||
};
|
||||
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));
|
||||
ASSUME(mLookAhead >= 0);
|
||||
auto side_begin = std::begin(mSideChain) + SamplesToDo;
|
||||
std::copy(side_begin, side_begin+mLookAhead, std::begin(mSideChain));
|
||||
}
|
||||
|
||||
|
||||
ALsizei GetCompressorLookAhead(const Compressor *Comp)
|
||||
{ return Comp->LookAhead; }
|
||||
|
||||
+32
-30
@@ -1,6 +1,8 @@
|
||||
#ifndef MASTERING_H
|
||||
#define MASTERING_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
@@ -21,8 +23,8 @@ struct SlidingHold;
|
||||
* http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
|
||||
*/
|
||||
struct Compressor {
|
||||
ALsizei NumChans;
|
||||
ALuint SampleRate;
|
||||
ALsizei mNumChans{0};
|
||||
ALuint mSampleRate{0u};
|
||||
|
||||
struct {
|
||||
bool Knee : 1;
|
||||
@@ -30,36 +32,41 @@ struct Compressor {
|
||||
bool Release : 1;
|
||||
bool PostGain : 1;
|
||||
bool Declip : 1;
|
||||
} Auto;
|
||||
} mAuto{};
|
||||
|
||||
ALsizei LookAhead;
|
||||
ALsizei mLookAhead{0};
|
||||
|
||||
ALfloat PreGain;
|
||||
ALfloat PostGain;
|
||||
ALfloat mPreGain{0.0f};
|
||||
ALfloat mPostGain{0.0f};
|
||||
|
||||
ALfloat Threshold;
|
||||
ALfloat Slope;
|
||||
ALfloat Knee;
|
||||
ALfloat mThreshold{0.0f};
|
||||
ALfloat mSlope{0.0f};
|
||||
ALfloat mKnee{0.0f};
|
||||
|
||||
ALfloat Attack;
|
||||
ALfloat Release;
|
||||
ALfloat mAttack{0.0f};
|
||||
ALfloat mRelease{0.0f};
|
||||
|
||||
alignas(16) ALfloat SideChain[2*BUFFERSIZE];
|
||||
alignas(16) ALfloat CrestFactor[BUFFERSIZE];
|
||||
alignas(16) ALfloat mSideChain[2*BUFFERSIZE]{};
|
||||
alignas(16) ALfloat mCrestFactor[BUFFERSIZE]{};
|
||||
|
||||
SlidingHold *Hold;
|
||||
ALfloat (*Delay)[BUFFERSIZE];
|
||||
ALsizei DelayIndex;
|
||||
SlidingHold *mHold{nullptr};
|
||||
ALfloat (*mDelay)[BUFFERSIZE]{nullptr};
|
||||
ALsizei mDelayIndex{0};
|
||||
|
||||
ALfloat CrestCoeff;
|
||||
ALfloat GainEstimate;
|
||||
ALfloat AdaptCoeff;
|
||||
ALfloat mCrestCoeff{0.0f};
|
||||
ALfloat mGainEstimate{0.0f};
|
||||
ALfloat mAdaptCoeff{0.0f};
|
||||
|
||||
ALfloat LastPeakSq;
|
||||
ALfloat LastRmsSq;
|
||||
ALfloat LastRelease;
|
||||
ALfloat LastAttack;
|
||||
ALfloat LastGainDev;
|
||||
ALfloat mLastPeakSq{0.0f};
|
||||
ALfloat mLastRmsSq{0.0f};
|
||||
ALfloat mLastRelease{0.0f};
|
||||
ALfloat mLastAttack{0.0f};
|
||||
ALfloat mLastGainDev{0.0f};
|
||||
|
||||
|
||||
~Compressor();
|
||||
void process(const ALsizei SamplesToDo, ALfloat (*OutBuffer)[BUFFERSIZE]);
|
||||
ALsizei getLookAhead() const noexcept { return mLookAhead; }
|
||||
|
||||
DEF_PLACE_NEWDEL()
|
||||
};
|
||||
@@ -88,7 +95,7 @@ struct Compressor {
|
||||
* ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
|
||||
* automating release time.
|
||||
*/
|
||||
Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
std::unique_ptr<Compressor> CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
const ALboolean AutoKnee, const ALboolean AutoAttack,
|
||||
const ALboolean AutoRelease, const ALboolean AutoPostGain,
|
||||
const ALboolean AutoDeclip, const ALfloat LookAheadTime,
|
||||
@@ -97,9 +104,4 @@ Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
const ALfloat Ratio, const ALfloat KneeDb,
|
||||
const ALfloat AttackTime, const ALfloat ReleaseTime);
|
||||
|
||||
void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo,
|
||||
ALfloat (*OutBuffer)[BUFFERSIZE]);
|
||||
|
||||
ALsizei GetCompressorLookAhead(const Compressor *Comp);
|
||||
|
||||
#endif /* MASTERING_H */
|
||||
|
||||
Reference in New Issue
Block a user