Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tweak master comp behaviour and parameters #695

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 9 additions & 120 deletions src/deluge/dsp/compressor/compressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@
Compressor::Compressor() {
status = EnvelopeStage::OFF;
lastValue = 2147483647;
envelopeOffset = ONE_Q31;
pos = 0;

attack = getParamFromUserValue(Param::Static::COMPRESSOR_ATTACK, 7);
release = getParamFromUserValue(Param::Static::COMPRESSOR_RELEASE, 28);
pendingHitStrength = 0;
Expand Down Expand Up @@ -82,14 +80,11 @@ void Compressor::registerHitRetrospectively(int32_t strength, uint32_t numSample
// If we're still in the release stage...
if (numSamplesSinceRelease < releaseStageLengthInSamples) {
pos = numSamplesSinceRelease * alteredRelease;
envelopeHeight = ONE_Q31 - envelopeOffset;
envelopeOffset = ONE_Q31;
status = EnvelopeStage::RELEASE;
}

// Or if we're past the release stage...
else {
envelopeOffset = ONE_Q31;
status = EnvelopeStage::OFF;
}
}
Expand Down Expand Up @@ -133,38 +128,30 @@ int32_t Compressor::render(uint16_t numSamples, int32_t shapeValue) {
int32_t newOffset = ONE_Q31 - pendingHitStrength;

pendingHitStrength = 0;
//envelope offset is the value we're attack/decaying to

// Only actually do anything if this hit is going to cause a bigger dip than we're already currently experiencing
if (newOffset < lastValue) {
envelopeOffset = newOffset;

// If attack is all the way down, jump directly to release stage
if (attack == attackRateTable[0] << 2) {
envelopeHeight = ONE_Q31 - envelopeOffset;
envelopeOffset = ONE_Q31;
pos = 0;
status = EnvelopeStage::RELEASE;
goto prepareForRelease;
}
else {

status = EnvelopeStage::ATTACK;
pos = 0;
}
status = EnvelopeStage::ATTACK;
envelopeHeight = lastValue - envelopeOffset;
pos = 0;
}
}

if (status == EnvelopeStage::ATTACK) {
pos += numSamples * getActualAttackRate();

if (pos >= 8388608) {
//if we're in follower mode then we just hold the value

envelopeHeight = ONE_Q31 - envelopeOffset;
envelopeOffset = ONE_Q31;
prepareForRelease:
pos = 0;
status = EnvelopeStage::RELEASE;

envelopeHeight = ONE_Q31 - envelopeOffset;
goto doRelease;
}
//lastValue = (multiply_32x32_rshift32(envelopeHeight, decayTable4[pos >> 13]) << 1) + envelopeOffset; // Goes down quickly at first. Bad
Expand Down Expand Up @@ -204,113 +191,15 @@ int32_t Compressor::render(uint16_t numSamples, int32_t shapeValue) {
preValue = straightness * (pos >> 8) + (getDecay8(8388608 - pos, 23) >> 16) * curvedness16;
}

lastValue = envelopeOffset - envelopeHeight + (multiply_32x32_rshift32(preValue, envelopeHeight) << 1);
lastValue = ONE_Q31 - envelopeHeight + (multiply_32x32_rshift32(preValue, envelopeHeight) << 1);

//lastValue = 2147483647 - (multiply_32x32_rshift32(decayTable8[pos >> 13], envelopeHeight) << 1); // Upside down exponential curve
//lastValue = 2147483647 - (((int64_t)((sineWave[((pos >> 14) + 256) & 1023] >> 1) + 1073741824) * (int64_t)envelopeHeight) >> 31); // Sine wave. Not great
//lastValue = (multiply_32x32_rshift32(pos * (pos >> 15), envelopeHeight) << 1); // Parabola. Doesn't "punch".
}

else { // Off or hold

doOff:
lastValue = envelopeOffset;
}

return lastValue - ONE_Q31;
}

int32_t Compressor::renderFollower(uint16_t numSamples, int32_t shapeValue) {

int32_t newOffset = ONE_Q31 - pendingHitStrength;

pendingHitStrength = 0;
//envelope offset is the value we're attack/decaying to
// Only actually do anything if this hit is going to cause a bigger dip than we're already currently experiencing
if (newOffset < lastValue) {
envelopeOffset = newOffset;
envelopeHeight = lastValue - envelopeOffset;
pos = 0;
if (status == EnvelopeStage::HOLD) {
status = EnvelopeStage::ATTACK;
}
else if (status != EnvelopeStage::ATTACK) {
status = EnvelopeStage::HOLD;
}
}
//or if we're working in follower mode, in which case we want to start releasing whenever the current hit strength is below the envelope level
else if (newOffset > envelopeOffset) {
envelopeOffset = newOffset;
envelopeHeight = newOffset - lastValue;
pos = 0;

if (status == EnvelopeStage::HOLD) {

status = EnvelopeStage::RELEASE;
}
else if (status != EnvelopeStage::RELEASE) {
status = EnvelopeStage::HOLD;
}
}

if (status == EnvelopeStage::ATTACK) {
pos += numSamples * getActualAttackRate();

if (pos >= 8388608) {
//if we're in follower mode then we just hold the value
status = EnvelopeStage::HOLD;
goto doOff;
}
//lastValue = (multiply_32x32_rshift32(envelopeHeight, decayTable4[pos >> 13]) << 1) + envelopeOffset; // Goes down quickly at first. Bad
//lastValue = (multiply_32x32_rshift32(envelopeHeight, 2147483647 - (pos << 8)) << 1) + envelopeOffset; // Straight line
lastValue = (multiply_32x32_rshift32(envelopeHeight, (ONE_Q31 - getDecay4(8388608 - pos, 23))) << 1)
+ envelopeOffset; // Goes down slowly at first. Great squishiness
//lastValue = (multiply_32x32_rshift32(envelopeHeight, (2147483647 - decayTable8[1023 - (pos >> 13)])) << 1) + envelopeOffset; // Even slower to accelerate. Loses punch slightly
//lastValue = (multiply_32x32_rshift32(envelopeHeight, (sineWave[((pos >> 14) + 256) & 1023] >> 1) + 1073741824) << 1) + envelopeOffset; // Sine wave. Sounds a bit flat
//lastValue = (multiply_32x32_rshift32(envelopeHeight, 2147483647 - pos * (pos >> 15)) << 1) + envelopeOffset; // Parabola. Not bad, but doesn't quite have punchiness
}
else if (status == EnvelopeStage::RELEASE) {
doRelease:
pos += numSamples * getActualReleaseRate();

if (pos >= 8388608) {
status = EnvelopeStage::HOLD;
goto doOff;
}

uint32_t positiveShapeValue = (uint32_t)shapeValue + 2147483648;

int32_t preValue;

// This would be the super simple case
// int32_t curvedness16 = (uint32_t)(positiveShapeValue + 32768) >> 16;

// And this is the better, more complicated case
int32_t curvedness16 = (positiveShapeValue >> 15) - (pos >> 7);
if (curvedness16 < 0) {
preValue = pos << 8;
}
else {
if (curvedness16 > 65536) {
curvedness16 = 65536;
}
int32_t straightness = 65536 - curvedness16;
preValue = straightness * (pos >> 8) + (getDecay8(8388608 - pos, 23) >> 16) * curvedness16;
}

lastValue = envelopeOffset - envelopeHeight + (multiply_32x32_rshift32(preValue, envelopeHeight) << 1);

lastValue = envelopeOffset
- (multiply_32x32_rshift32(envelopeHeight, (ONE_Q31 - getDecay8(8388608 - pos, 23)))
<< 1); // Upside down exponential curve
//lastValue = 2147483647 - (((int64_t)((sineWave[((pos >> 14) + 256) & 1023] >> 1) + 1073741824) * (int64_t)envelopeHeight) >> 31); // Sine wave. Not great
//lastValue = (multiply_32x32_rshift32(pos * (pos >> 15), envelopeHeight) << 1); // Parabola. Doesn't "punch".
}

else { // Off or hold

else { // Off
doOff:
lastValue = envelopeOffset;
lastValue = ONE_Q31;
}

return lastValue - ONE_Q31;
Expand Down
2 changes: 1 addition & 1 deletion src/deluge/dsp/compressor/compressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Compressor {
public:
Compressor();
void cloneFrom(Compressor* other);

EnvelopeStage status;
uint32_t pos;
int32_t lastValue;
Expand All @@ -41,7 +42,6 @@ class Compressor {
SyncLevel syncLevel; // Basically, 0 is off, max value is 9. Higher numbers are shorter intervals (higher speed).

int32_t render(uint16_t numSamples, int32_t shapeValue);
int32_t renderFollower(uint16_t numSamples, int32_t shapeValue);
void registerHit(int32_t strength);
void registerHitRetrospectively(int32_t strength, uint32_t numSamplesAgo);

Expand Down
82 changes: 37 additions & 45 deletions src/deluge/dsp/master_compressor/master_compressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,14 @@
#include "processing/engines/audio_engine.h"
#include "util/fast_fixed_math.h"
MasterCompressor::MasterCompressor() {
//compressor.setAttack((float)attack / 100.0);
attack = attackRateTable[1];
release = releaseRateTable[15];
//compressor.setRelease((float)release / 100.0);
//compressor.setThresh((float)threshold / 100.0);
//compressor.setRatio(1.0 / ((float)ratio / 100.0));
shape = getParamFromUserValue(Param::Unpatched::COMPRESSOR_SHAPE, 0);

//an appropriate range is 0-50*one q 15
threshold = ONE_Q31;
rawThreshold = 0;

//this is about a 1:1 ratio
ratio = ONE_Q31 >> 1;
syncLevel = SyncLevel::SYNC_LEVEL_NONE;
thresholdKnobPos = 0;

//this is 2:1
ratioKnobPos = 0;

currentVolumeL = 0;
currentVolumeR = 0;
//auto make up gain
Expand All @@ -59,39 +53,27 @@ void MasterCompressor::updateER() {
else {
songVolume = 16;
}
threshdb = songVolume * (threshold / ONE_Q31f);
threshdb = songVolume * threshold;
//16 is about the level of a single synth voice at max volume
er = std::max<float>((songVolume - threshdb - 2) * (float(ratio) / ONE_Q31), 0);
er = std::max<float>((songVolume - threshdb - 2) * ratio, 0);
}

void MasterCompressor::render(StereoSample* buffer, uint16_t numSamples, q31_t volAdjustL, q31_t volAdjustR) {
ratio = (rawRatio >> 1) + (3 << 28);
threshold = ONE_Q31 - (rawThreshold >> 1);
//we update this every time since we won't know if the song volume changed
updateER();

q31_t over = std::max<float>(0, (meanVolume - threshdb) / 21.0) * ONE_Q31;
float over = std::max<float>(0, (rms - threshdb));

//add some extra reduction if we're into clipping
float out = runEnvelope(over, numSamples);

if (over > 0) {
registerHit(over);
}
out = Compressor::renderFollower(numSamples, shape);
out = (multiply_32x32_rshift32(out, ratio) << 1);
//out = multiply_32x32_rshift32(out, ratio) << 1;

//21 is the max internal volume (i.e. one_q31)
//min ratio is 8 up to 1 (i.e. infinity/brick wall, 1 db reduction per db over)
//base is arbitrary for scale, important part is the shape
//this will be negative
float reduction = 21 * (out / ONE_Q31f);
float reduction = -out * ratio;

//this is the most gain available without overflow
float dbGain = 0.85 + er + reduction;

float gain = exp((dbGain));
gain = std::min<float>(gain, 31);
lastGain = dbGain;

float finalVolumeL = gain * float(volAdjustL >> 9);
float finalVolumeR = gain * float(volAdjustR >> 9);

Expand All @@ -112,9 +94,19 @@ void MasterCompressor::render(StereoSample* buffer, uint16_t numSamples, q31_t v
} while (++thisSample != bufferEnd);
//for LEDs
//9 converts to dB, quadrupled for display range since a 30db reduction is basically killing the signal
gainReduction = std::clamp<int32_t>(-(reduction) * 9 * 4, 0, 127);
gainReduction = std::clamp<int32_t>(-(reduction)*9 * 4, 0, 127);
//calc compression for next round (feedback compressor)
meanVolume = calc_rms(buffer, numSamples);
rms = calc_rms(buffer, numSamples);
}

float MasterCompressor::runEnvelope(float in, float numSamples) {
if (in > state) {
state = in + exp(a_ * numSamples) * (state - in);
}
else {
state = in + exp(r_ * numSamples) * (state - in);
}
return state;
}

//output range is 0-21 (2^31)
Expand All @@ -132,22 +124,22 @@ float MasterCompressor::calc_rms(StereoSample* buffer, uint16_t numSamples) {
} while (++thisSample != bufferEnd);

float ns = float(numSamples);
float rms = ONE_Q31 * sqrt((float(sum) / ONE_Q31f) / ns);
// float dc = 0;

// //warning this is not good math but it's pretty close and way cheaper than doing it properly
// mean = rms - dc / 1.4f;
mean = (rms + lastMean) / 2;
mean = (float(sum) / ONE_Q31f) / ns;
//warning this is not good math but it's pretty close and way cheaper than doing it properly
//good math would use a long FIR, this is a one pole IIR instead
//the more samples we have, the more weight we put on the current mean to avoid response slowing down
//at high cpu loads
mean = (mean * ns + lastMean) / (1 + ns);
float rms = ONE_Q31 * sqrt(mean);

float logmean = std::log(std::max(mean, 1.0f));
float logmean = std::log(std::max(rms, 1.0f));

return logmean;
}

void MasterCompressor::setup(int32_t a, int32_t r, int32_t t, int32_t rat) {
attack = a;
release = r;
rawThreshold = t;
rawRatio = rat;
updateER();
setAttack(a);
setRelease(r);
setThreshold(t);
setRatio(rat);
}
Loading