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 all 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
67 changes: 15 additions & 52 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;
follower = false;
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 @@ -129,65 +124,35 @@ int32_t Compressor::getActualReleaseRate() {
int32_t Compressor::render(uint16_t numSamples, int32_t shapeValue) {

// Initial hit detected...
if (pendingHitStrength != 0 || follower) {
if (pendingHitStrength != 0) {
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;
}
else {
if (!follower || status == EnvelopeStage::HOLD) {
status = EnvelopeStage::ATTACK;
pos = 0;
}
else if (status != EnvelopeStage::ATTACK) {
status = EnvelopeStage::HOLD;
}

envelopeHeight = lastValue - envelopeOffset;
}
}
//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 (follower && newOffset > envelopeOffset) {
envelopeOffset = newOffset;
envelopeHeight = newOffset - lastValue;
if (status == EnvelopeStage::HOLD) {
pos = 0;
status = EnvelopeStage::RELEASE;
}
else if (status != EnvelopeStage::RELEASE) {
status = EnvelopeStage::HOLD;
goto prepareForRelease;
}

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
if (!follower) {
envelopeHeight = ONE_Q31 - envelopeOffset;
envelopeOffset = ONE_Q31;
prepareForRelease:
pos = 0;
status = EnvelopeStage::RELEASE;

goto doRelease;
}
else {
status = EnvelopeStage::HOLD;
goto doOff;
}
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
//lastValue = (multiply_32x32_rshift32(envelopeHeight, 2147483647 - (pos << 8)) << 1) + envelopeOffset; // Straight line
Expand Down Expand Up @@ -226,17 +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

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,7 +26,7 @@ class Compressor {
public:
Compressor();
void cloneFrom(Compressor* other);
bool follower;

EnvelopeStage status;
uint32_t pos;
int32_t lastValue;
Expand Down
84 changes: 38 additions & 46 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[2] << 2;
release = releaseRateTable[5] << 2;
//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, 1);

//an appropriate range is 0-50*one q 15
threshold = ONE_Q31;
rawThreshold = 0;
follower = true;
//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 @@ -54,44 +48,32 @@ void MasterCompressor::updateER() {
134217728, cableToLinearParamShortcut(currentSong->paramManager.getUnpatchedParamSet()->getValue(
Param::Unpatched::GlobalEffectable::VOLUME)))
>> 1;
songVolume = std::log(volumePostFX);
songVolume = std::log(volumePostFX) - 2;
}
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;
//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) * ONE_Q31;
q31_t clip = std::max<float>(0, (meanVolume - 18) / 21) * ONE_Q31;
//add some extra reduction if we're into clipping
float over = std::max<float>(0, (rms - threshdb));

if (over > 0) {
registerHit(over);
}
out = Compressor::render(numSamples, shape);
out = (multiply_32x32_rshift32(out, ratio) << 1) - (multiply_32x32_rshift32(clip, ONE_Q31 - ratio));
//out = multiply_32x32_rshift32(out, ratio) << 1;
float out = runEnvelope(over, numSamples);

//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 @@ -114,7 +96,17 @@ void MasterCompressor::render(StereoSample* buffer, uint16_t numSamples, q31_t v
//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);
//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 @@ -126,28 +118,28 @@ float MasterCompressor::calc_rms(StereoSample* buffer, uint16_t numSamples) {
q31_t offset = 0; //to remove dc offset
float lastMean = mean;
do {
q31_t s = std::abs(thisSample->l) + std::abs(thisSample->r);
q31_t s = std::max(std::abs(thisSample->l), std::abs(thisSample->r));
sum += multiply_32x32_rshift32(s, s) << 1;
offset += thisSample->l;

} while (++thisSample != bufferEnd);

float ns = float(numSamples);
float rms = ONE_Q31 * sqrt((float(sum) / ONE_Q31f) / ns);
float dc = std::abs(offset) / ns;
mean = (float(sum) / ONE_Q31f) / ns;
//warning this is not good math but it's pretty close and way cheaper than doing it properly
mean = rms - dc / 1.4f;
mean = std::max(mean, 1.0f);
//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((mean + lastMean) / 2);
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);
}
71 changes: 57 additions & 14 deletions src/deluge/dsp/master_compressor/master_compressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,72 @@
#include <cmath>
#include <cstdint>

class MasterCompressor : public Compressor {
class MasterCompressor {
public:
MasterCompressor();
void setup(int32_t attack, int32_t release, int32_t threshold, int32_t ratio);

void render(StereoSample* buffer, uint16_t numSamples, q31_t volAdjustL, q31_t volAdjustR);
float runEnvelope(float in, float numSamples);
//attack/release in range 0 to 2^31
inline q31_t getAttack() { return attackKnobPos; }
inline int32_t getAttackMS() { return attackMS; }
int32_t setAttack(q31_t attack) {
//this exp will be between 1 and 7ish, half the knob range is about 2.5
attackMS = 0.5 + (exp(2 * float(attack) / ONE_Q31f) - 1) * 10;
a_ = (-1000.0 / 44100) / attackMS;
attackKnobPos = attack;
return attackMS;
};
inline q31_t getRelease() { return releaseKnobPos; }
inline int32_t getReleaseMS() { return releaseMS; }
int32_t setRelease(q31_t release) {
//this exp will be between 1 and 7ish, half the knob range is about 2.5
releaseMS = 50 + (exp(2 * float(release) / ONE_Q31f) - 1) * 50;
r_ = (-1000.0 / 44100) / releaseMS;
releaseKnobPos = release;
return releaseMS;
};
q31_t getThreshold() { return thresholdKnobPos; }
void setThreshold(q31_t t) {
thresholdKnobPos = t;
threshold = 1 - 0.8 * (float(thresholdKnobPos) / ONE_Q31f);
updateER();
}
q31_t getRatio() { return ratioKnobPos; }
q31_t setRatio(q31_t rat) {
ratioKnobPos = rat;
ratio = 0.5 + (float(ratioKnobPos) / ONE_Q31f) / 2;
return 1 / (1 - ratio);
}

void updateER();
float calc_rms(StereoSample* buffer, uint16_t numSamples);
uint8_t gainReduction;
bool dither;
q31_t rawRatio;
q31_t rawThreshold;
q31_t threshold;
q31_t shape;
q31_t ratio;
q31_t out;
q31_t over;
q31_t currentVolumeL;
q31_t currentVolumeR;

float meanVolume;
float mean;
float lastGain;
private:
//parameters in use
float a_;
float r_;
float ratio;
float er;
float threshdb;
float threshold;

//state
float state;
q31_t currentVolumeL;
q31_t currentVolumeR;
float rms;
float mean;

//for display
float attackMS;
float releaseMS;

//raw knob positions
q31_t thresholdKnobPos;
q31_t ratioKnobPos;
q31_t attackKnobPos;
q31_t releaseKnobPos;
};
Loading