From 65de7b73d4dd1755287a90a762175ec97e3cd9c5 Mon Sep 17 00:00:00 2001 From: m-m-adams Date: Tue, 7 Nov 2023 00:16:25 -0500 Subject: [PATCH 1/9] tweak master comp behaviour and parameters --- src/deluge/dsp/compressor/compressor.cpp | 140 +++++++++++++----- src/deluge/dsp/compressor/compressor.h | 2 +- .../master_compressor/master_compressor.cpp | 36 ++--- .../global_effectable/global_effectable.cpp | 8 +- src/deluge/model/song/song.cpp | 16 +- .../processing/engines/audio_engine.cpp | 8 +- 6 files changed, 142 insertions(+), 68 deletions(-) diff --git a/src/deluge/dsp/compressor/compressor.cpp b/src/deluge/dsp/compressor/compressor.cpp index a61ce5ef17..81a54f84da 100644 --- a/src/deluge/dsp/compressor/compressor.cpp +++ b/src/deluge/dsp/compressor/compressor.cpp @@ -27,7 +27,7 @@ Compressor::Compressor() { 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; @@ -129,7 +129,7 @@ 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; @@ -146,48 +146,26 @@ int32_t Compressor::render(uint16_t numSamples, int32_t shapeValue) { 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) { + + status = EnvelopeStage::ATTACK; pos = 0; - 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 - if (!follower) { - envelopeHeight = ONE_Q31 - envelopeOffset; - envelopeOffset = ONE_Q31; + + envelopeHeight = ONE_Q31 - envelopeOffset; + envelopeOffset = ONE_Q31; prepareForRelease: - pos = 0; - status = EnvelopeStage::RELEASE; + pos = 0; + status = EnvelopeStage::RELEASE; - goto doRelease; - } - else { - status = EnvelopeStage::HOLD; - goto doOff; - } + 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 @@ -241,3 +219,99 @@ int32_t Compressor::render(uint16_t numSamples, int32_t shapeValue) { 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 + +doOff: + lastValue = envelopeOffset; + } + + return lastValue - ONE_Q31; +} diff --git a/src/deluge/dsp/compressor/compressor.h b/src/deluge/dsp/compressor/compressor.h index b9c81d9309..78470afcc8 100644 --- a/src/deluge/dsp/compressor/compressor.h +++ b/src/deluge/dsp/compressor/compressor.h @@ -26,7 +26,6 @@ class Compressor { public: Compressor(); void cloneFrom(Compressor* other); - bool follower; EnvelopeStage status; uint32_t pos; int32_t lastValue; @@ -42,6 +41,7 @@ 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); diff --git a/src/deluge/dsp/master_compressor/master_compressor.cpp b/src/deluge/dsp/master_compressor/master_compressor.cpp index ee15577fa4..9e19fb955b 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.cpp +++ b/src/deluge/dsp/master_compressor/master_compressor.cpp @@ -24,16 +24,16 @@ #include "util/fast_fixed_math.h" MasterCompressor::MasterCompressor() { //compressor.setAttack((float)attack / 100.0); - attack = attackRateTable[2] << 2; - release = releaseRateTable[5] << 2; + 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, 1); + shape = getParamFromUserValue(Param::Unpatched::COMPRESSOR_SHAPE, 0); //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; @@ -54,7 +54,7 @@ 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; @@ -66,18 +66,18 @@ void MasterCompressor::updateER() { void MasterCompressor::render(StereoSample* buffer, uint16_t numSamples, q31_t volAdjustL, q31_t volAdjustR) { ratio = (rawRatio >> 1) + (3 << 28); - threshold = ONE_Q31 - rawThreshold; + threshold = ONE_Q31 - (rawThreshold >> 1); updateER(); - q31_t over = std::max(0, (meanVolume - threshdb) / 21) * ONE_Q31; - q31_t clip = std::max(0, (meanVolume - 18) / 21) * ONE_Q31; + q31_t over = std::max(0, (meanVolume - threshdb) / 21.0) * ONE_Q31; + //add some extra reduction if we're into clipping 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 = 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) @@ -112,7 +112,7 @@ 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(-(reduction)*9 * 4, 0, 127); + gainReduction = std::clamp(-(reduction) * 9 * 4, 0, 127); //calc compression for next round (feedback compressor) meanVolume = calc_rms(buffer, numSamples); } @@ -126,20 +126,20 @@ 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; - //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); + // 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; - float logmean = std::log((mean + lastMean) / 2); + float logmean = std::log(std::max(mean, 1.0f)); return logmean; } diff --git a/src/deluge/model/global_effectable/global_effectable.cpp b/src/deluge/model/global_effectable/global_effectable.cpp index 053cccc3c9..8bbe239b67 100644 --- a/src/deluge/model/global_effectable/global_effectable.cpp +++ b/src/deluge/model/global_effectable/global_effectable.cpp @@ -313,12 +313,12 @@ int32_t GlobalEffectable::getKnobPosForNonExistentParam(int32_t whichModEncoder, break; case CompParam::ATTACK: - current = getLookupIndexFromValue(AudioEngine::mastercompressor.attack >> 2, attackRateTable, 50); + current = getLookupIndexFromValue(AudioEngine::mastercompressor.attack, attackRateTable, 50) << 2; displayLevel = (current * 128) / 50; break; case CompParam::RELEASE: - current = getLookupIndexFromValue(AudioEngine::mastercompressor.release >> 1, releaseRateTable, 50); + current = getLookupIndexFromValue(AudioEngine::mastercompressor.release, releaseRateTable, 50) << 2; displayLevel = (current * 128) / 50; break; } @@ -369,13 +369,13 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse case CompParam::RELEASE: - current = getLookupIndexFromValue(AudioEngine::mastercompressor.release, releaseRateTable, 50); + current = getLookupIndexFromValue(AudioEngine::mastercompressor.release >> 2, releaseRateTable, 50); current += offset; current = std::clamp(current, 0, 50); displayLevel = current; ledLevel = (displayLevel * 128) / 50; - AudioEngine::mastercompressor.release = releaseRateTable[current]; + AudioEngine::mastercompressor.release = releaseRateTable[current] << 2; break; } diff --git a/src/deluge/model/song/song.cpp b/src/deluge/model/song/song.cpp index c183f80ca6..caa32f2110 100644 --- a/src/deluge/model/song/song.cpp +++ b/src/deluge/model/song/song.cpp @@ -136,10 +136,10 @@ Song::Song() : backedUpParamManagers(sizeof(BackedUpParamManager)) { reverbCompressorShape = -601295438; reverbCompressorSync = SYNC_LEVEL_8TH; - masterCompressorAttack = attackRateTable[2] << 2; - masterCompressorRelease = releaseRateTable[5] << 2; + masterCompressorAttack = attackRateTable[5] << 2; + masterCompressorRelease = releaseRateTable[15] << 1; masterCompressorThresh = 0; - masterCompressorRatio = ONE_Q31 >> 1; + masterCompressorRatio = 0; AudioEngine::mastercompressor.gainReduction = 0.0; dirPath.set("SONGS"); @@ -1122,7 +1122,7 @@ void Song::writeToFile() { storageManager.writeClosingTag("reverb"); - storageManager.writeOpeningTagBeginning("masterCompressor"); + storageManager.writeOpeningTagBeginning("songCompressor"); int32_t attack = AudioEngine::mastercompressor.attack; int32_t release = AudioEngine::mastercompressor.release; int32_t thresh = AudioEngine::mastercompressor.rawThreshold; @@ -1476,7 +1476,7 @@ int32_t Song::readFromFile() { storageManager.exitTag("affectEntire"); } - else if (!strcmp(tagName, "masterCompressor")) { + else if (!strcmp(tagName, "songCompressor")) { while (*(tagName = storageManager.readNextTagOrAttributeName())) { if (!strcmp(tagName, "attack")) { //ms masterCompressorAttack = storageManager.readTagOrAttributeValueInt(); @@ -1498,7 +1498,7 @@ int32_t Song::readFromFile() { storageManager.exitTag(tagName); } } - storageManager.exitTag("masterCompressor"); + storageManager.exitTag("songCompressor"); } else if (!strcmp(tagName, "modeNotes")) { @@ -2670,7 +2670,7 @@ int32_t Song::getCurrentPresetScale() { // If we're here, must be this one! return p; -notThisOne : {} +notThisOne: {} } return 255; @@ -4559,7 +4559,7 @@ Instrument* Song::changeInstrumentType(Instrument* oldInstrument, InstrumentType return NULL; } -gotAnInstrument : {} +gotAnInstrument: {} } // Synth or Kit diff --git a/src/deluge/processing/engines/audio_engine.cpp b/src/deluge/processing/engines/audio_engine.cpp index 81ac68b0a1..eea303804f 100644 --- a/src/deluge/processing/engines/audio_engine.cpp +++ b/src/deluge/processing/engines/audio_engine.cpp @@ -404,10 +404,10 @@ void routine() { smoothedSamples = numSamples; if (numSamplesLastTime < numSamples) { - Debug::print("rendered "); - Debug::print(numSamplesLastTime); - Debug::print(" samples but output "); - Debug::println(numSamples); + logAction("rendered "); + logAction(numSamplesLastTime); + logAction(" samples but output "); + logAction(numSamples); } // Consider direness and culling - before increasing the number of samples From 6da458b05d3288be03ff3ebdc667f47d09114225 Mon Sep 17 00:00:00 2001 From: m-m-adams Date: Wed, 8 Nov 2023 14:26:56 -0500 Subject: [PATCH 2/9] switch to float based exponential envelopes --- src/deluge/dsp/compressor/compressor.cpp | 7 +- .../master_compressor/master_compressor.cpp | 73 +++++++++---------- .../dsp/master_compressor/master_compressor.h | 56 ++++++++++---- .../global_effectable/global_effectable.cpp | 50 ++++++------- src/deluge/model/song/song.cpp | 16 ++-- src/deluge/util/fixedpoint.h | 2 +- 6 files changed, 111 insertions(+), 93 deletions(-) diff --git a/src/deluge/dsp/compressor/compressor.cpp b/src/deluge/dsp/compressor/compressor.cpp index 81a54f84da..c75669db05 100644 --- a/src/deluge/dsp/compressor/compressor.cpp +++ b/src/deluge/dsp/compressor/compressor.cpp @@ -300,11 +300,8 @@ int32_t Compressor::renderFollower(uint16_t numSamples, int32_t shapeValue) { 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". + lastValue = + envelopeOffset - (multiply_32x32_rshift32(envelopeHeight, (ONE_Q31 - getDecay4(8388608 - pos, 23))) << 1); } else { // Off or hold diff --git a/src/deluge/dsp/master_compressor/master_compressor.cpp b/src/deluge/dsp/master_compressor/master_compressor.cpp index 9e19fb955b..540be3507d 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.cpp +++ b/src/deluge/dsp/master_compressor/master_compressor.cpp @@ -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; + //this is 2:1 + rawRatio = 0; + currentVolumeL = 0; currentVolumeR = 0; //auto make up gain @@ -59,39 +53,34 @@ 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((songVolume - threshdb - 2) * (float(ratio) / ONE_Q31), 0); + er = std::max((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); + updateER(); - q31_t over = std::max(0, (meanVolume - threshdb) / 21.0) * ONE_Q31; + float over = std::max(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 = out * ratio; //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; //this is the most gain available without overflow float dbGain = 0.85 + er + reduction; float gain = exp((dbGain)); gain = std::min(gain, 31); - lastGain = dbGain; + float finalVolumeL = gain * float(volAdjustL >> 9); float finalVolumeR = gain * float(volAdjustR >> 9); @@ -112,9 +101,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(-(reduction) * 9 * 4, 0, 127); + gainReduction = std::clamp(-(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) @@ -132,22 +131,20 @@ 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 + mean = (mean + lastMean) / 2; + 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); } diff --git a/src/deluge/dsp/master_compressor/master_compressor.h b/src/deluge/dsp/master_compressor/master_compressor.h index 4045021e68..392f425e9c 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.h +++ b/src/deluge/dsp/master_compressor/master_compressor.h @@ -27,29 +27,57 @@ #include #include -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 ms + int32_t getAttack() { return attackms; } + void setAttack(int32_t attack) { + a_ = (-1000.0 / 44100) / (float(attack)); + attackms = attack; + }; + int32_t getRelease() { return releasems; } + void setRelease(int32_t release) { + r_ = (-1000.0 / 44100) / (float(10 * release)); + releasems = release; + }; + q31_t getThreshold() { return rawThreshold; } + void setThreshold(q31_t t) { + rawThreshold = t; + threshold = 1 - ((rawThreshold >> 1) / ONE_Q31f); + updateER(); + } + q31_t getRatio() { return rawRatio; } + void setRatio(q31_t rat) { + rawRatio = rat; + ratio = 0.5 + (float(rawRatio) / ONE_Q31f) / 2; + updateER(); + } + 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: + float a_; + float r_; + float ratio; float er; + float threshdb; + float state; + float rms; + float mean; + q31_t currentVolumeL; + q31_t currentVolumeR; + + q31_t rawThreshold; + q31_t rawRatio; + int32_t attackms; + int32_t releasems; + float threshold; }; diff --git a/src/deluge/model/global_effectable/global_effectable.cpp b/src/deluge/model/global_effectable/global_effectable.cpp index 8bbe239b67..5ca54c9cef 100644 --- a/src/deluge/model/global_effectable/global_effectable.cpp +++ b/src/deluge/model/global_effectable/global_effectable.cpp @@ -295,36 +295,34 @@ bool GlobalEffectable::modEncoderButtonAction(uint8_t whichModEncoder, bool on, } int32_t GlobalEffectable::getKnobPosForNonExistentParam(int32_t whichModEncoder, ModelStackWithAutoParam* modelStack) { - int displayLevel = -64; + int current; if (*getModKnobMode() == 4) { - int current; //this is only reachable in comp editing mode, otherwise it's an existent param if (whichModEncoder == 1) { //sidechain (threshold) - current = AudioEngine::mastercompressor.rawThreshold >> 24; - displayLevel = current; + current = (AudioEngine::mastercompressor.getThreshold() >> 24); } else if (whichModEncoder == 0) { switch (currentCompParam) { case CompParam::RATIO: - current = AudioEngine::mastercompressor.rawRatio >> 24; - displayLevel = current; + current = (AudioEngine::mastercompressor.getRatio() >> 24); + break; case CompParam::ATTACK: - current = getLookupIndexFromValue(AudioEngine::mastercompressor.attack, attackRateTable, 50) << 2; - displayLevel = (current * 128) / 50; + current = AudioEngine::mastercompressor.getAttack(); + break; case CompParam::RELEASE: - current = getLookupIndexFromValue(AudioEngine::mastercompressor.release, releaseRateTable, 50) << 2; - displayLevel = (current * 128) / 50; + current = AudioEngine::mastercompressor.getRelease(); + break; } } } - return displayLevel - 64; + return current - 64; } ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offset, int32_t whichModEncoder, @@ -335,48 +333,46 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse int ledLevel; //this is only reachable in comp editing mode, otherwise it's an existent param if (whichModEncoder == 1) { //sidechain (threshold) - current = (AudioEngine::mastercompressor.rawThreshold >> 24) - 64; + current = (AudioEngine::mastercompressor.getThreshold() >> 24) - 64; current += offset; current = std::clamp(current, -64, 64); ledLevel = (64 + current); displayLevel = ((ledLevel)*kMaxMenuValue) / 128; - AudioEngine::mastercompressor.rawThreshold = lshiftAndSaturate<24>(current + 64); + AudioEngine::mastercompressor.setThreshold(lshiftAndSaturate<24>(current + 64)); indicator_leds::setKnobIndicatorLevel(1, ledLevel); } else if (whichModEncoder == 0) { switch (currentCompParam) { case CompParam::RATIO: - current = (AudioEngine::mastercompressor.rawRatio >> 24) - 64; + current = (AudioEngine::mastercompressor.getRatio() >> 24) - 64; current += offset; //this range is ratio of 2 to 20 current = std::clamp(current, -64, 64); ledLevel = (64 + current); displayLevel = ((ledLevel)*kMaxMenuValue) / 128; - AudioEngine::mastercompressor.rawRatio = lshiftAndSaturate<24>(current + 64); + AudioEngine::mastercompressor.setRatio(lshiftAndSaturate<24>(current + 64)); break; case CompParam::ATTACK: - current = getLookupIndexFromValue(AudioEngine::mastercompressor.attack >> 2, attackRateTable, 50); + current = AudioEngine::mastercompressor.getAttack() - 64; current += offset; - current = std::clamp(current, 1, 50); - displayLevel = current; - ledLevel = (displayLevel * 128) / 50; + current = std::clamp(current, -63, 64); + ledLevel = (64 + current); + displayLevel = ((ledLevel)*kMaxMenuValue) / 128; - AudioEngine::mastercompressor.attack = attackRateTable[current] << 2; + AudioEngine::mastercompressor.setAttack(current + 64); break; case CompParam::RELEASE: - - current = getLookupIndexFromValue(AudioEngine::mastercompressor.release >> 2, releaseRateTable, 50); + current = AudioEngine::mastercompressor.getRelease() - 64; current += offset; - current = std::clamp(current, 0, 50); - displayLevel = current; - ledLevel = (displayLevel * 128) / 50; - - AudioEngine::mastercompressor.release = releaseRateTable[current] << 2; + current = std::clamp(current, -63, 64); + ledLevel = (64 + current); + displayLevel = ((ledLevel)*kMaxMenuValue) / 128; + AudioEngine::mastercompressor.setRelease(current + 64); break; } indicator_leds::setKnobIndicatorLevel(0, ledLevel); diff --git a/src/deluge/model/song/song.cpp b/src/deluge/model/song/song.cpp index caa32f2110..cadbb6e389 100644 --- a/src/deluge/model/song/song.cpp +++ b/src/deluge/model/song/song.cpp @@ -136,8 +136,8 @@ Song::Song() : backedUpParamManagers(sizeof(BackedUpParamManager)) { reverbCompressorShape = -601295438; reverbCompressorSync = SYNC_LEVEL_8TH; - masterCompressorAttack = attackRateTable[5] << 2; - masterCompressorRelease = releaseRateTable[15] << 1; + masterCompressorAttack = 10; + masterCompressorRelease = 100; masterCompressorThresh = 0; masterCompressorRatio = 0; AudioEngine::mastercompressor.gainReduction = 0.0; @@ -1123,10 +1123,10 @@ void Song::writeToFile() { storageManager.writeClosingTag("reverb"); storageManager.writeOpeningTagBeginning("songCompressor"); - int32_t attack = AudioEngine::mastercompressor.attack; - int32_t release = AudioEngine::mastercompressor.release; - int32_t thresh = AudioEngine::mastercompressor.rawThreshold; - int32_t ratio = AudioEngine::mastercompressor.rawRatio; + int32_t attack = AudioEngine::mastercompressor.getAttack(); + int32_t release = AudioEngine::mastercompressor.getRelease(); + int32_t thresh = AudioEngine::mastercompressor.getThreshold(); + int32_t ratio = AudioEngine::mastercompressor.getRatio(); storageManager.writeAttribute("attack", attack); storageManager.writeAttribute("release", release); @@ -2670,7 +2670,7 @@ int32_t Song::getCurrentPresetScale() { // If we're here, must be this one! return p; -notThisOne: {} +notThisOne : {} } return 255; @@ -4559,7 +4559,7 @@ Instrument* Song::changeInstrumentType(Instrument* oldInstrument, InstrumentType return NULL; } -gotAnInstrument: {} +gotAnInstrument : {} } // Synth or Kit diff --git a/src/deluge/util/fixedpoint.h b/src/deluge/util/fixedpoint.h index 0182f5dadf..762d11a078 100644 --- a/src/deluge/util/fixedpoint.h +++ b/src/deluge/util/fixedpoint.h @@ -22,7 +22,7 @@ typedef int32_t q31_t; #define ONE_Q31 2147483647 -#define ONE_Q31f float(ONE_Q31) +#define ONE_Q31f 2147483647.0 #define ONE_Q15 65536 #define NEGATIVE_ONE_Q31 -2147483648 #define ONE_OVER_SQRT2_Q31 1518500250 From 9d5cedb63a7803dd5406db66ec6528803f0ca213 Mon Sep 17 00:00:00 2001 From: m-m-adams Date: Wed, 8 Nov 2023 20:33:55 -0500 Subject: [PATCH 3/9] show actual attack and release in ms exponential knob scaling --- .../master_compressor/master_compressor.cpp | 15 ++--- .../dsp/master_compressor/master_compressor.h | 61 ++++++++++++------- .../global_effectable/global_effectable.cpp | 18 +++--- src/deluge/model/song/song.cpp | 4 +- 4 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/deluge/dsp/master_compressor/master_compressor.cpp b/src/deluge/dsp/master_compressor/master_compressor.cpp index 540be3507d..737e1ea3a2 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.cpp +++ b/src/deluge/dsp/master_compressor/master_compressor.cpp @@ -26,10 +26,10 @@ MasterCompressor::MasterCompressor() { //an appropriate range is 0-50*one q 15 - rawThreshold = 0; + thresholdKnobPos = 0; //this is 2:1 - rawRatio = 0; + ratioKnobPos = 0; currentVolumeL = 0; currentVolumeR = 0; @@ -59,21 +59,14 @@ void MasterCompressor::updateER() { } void MasterCompressor::render(StereoSample* buffer, uint16_t numSamples, q31_t volAdjustL, q31_t volAdjustR) { - + //we update this every time since we won't know if the song volume changed updateER(); float over = std::max(0, (rms - threshdb)); float out = runEnvelope(over, numSamples); - out = out * ratio; - //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 = -out; + float reduction = -out * ratio; //this is the most gain available without overflow float dbGain = 0.85 + er + reduction; diff --git a/src/deluge/dsp/master_compressor/master_compressor.h b/src/deluge/dsp/master_compressor/master_compressor.h index 392f425e9c..1a54d859c4 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.h +++ b/src/deluge/dsp/master_compressor/master_compressor.h @@ -34,27 +34,35 @@ class MasterCompressor { void render(StereoSample* buffer, uint16_t numSamples, q31_t volAdjustL, q31_t volAdjustR); float runEnvelope(float in, float numSamples); - //attack/release in ms - int32_t getAttack() { return attackms; } - void setAttack(int32_t attack) { - a_ = (-1000.0 / 44100) / (float(attack)); - attackms = attack; + //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; }; - int32_t getRelease() { return releasems; } - void setRelease(int32_t release) { - r_ = (-1000.0 / 44100) / (float(10 * release)); - releasems = release; + 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 rawThreshold; } + q31_t getThreshold() { return thresholdKnobPos; } void setThreshold(q31_t t) { - rawThreshold = t; - threshold = 1 - ((rawThreshold >> 1) / ONE_Q31f); + thresholdKnobPos = t; + threshold = 1 - ((thresholdKnobPos >> 1) / ONE_Q31f); updateER(); } - q31_t getRatio() { return rawRatio; } + q31_t getRatio() { return ratioKnobPos; } void setRatio(q31_t rat) { - rawRatio = rat; - ratio = 0.5 + (float(rawRatio) / ONE_Q31f) / 2; + ratioKnobPos = rat; + ratio = 0.5 + (float(ratioKnobPos) / ONE_Q31f) / 2; updateER(); } @@ -63,21 +71,28 @@ class MasterCompressor { uint8_t gainReduction; private: + //parameters in use float a_; float r_; float ratio; float er; - float threshdb; + float threshold; + + //state float state; - float rms; - float mean; q31_t currentVolumeL; q31_t currentVolumeR; + float rms; + float mean; - q31_t rawThreshold; - q31_t rawRatio; - int32_t attackms; - int32_t releasems; - float threshold; + //for display + float attackMS; + float releaseMS; + + //raw knob positions + q31_t thresholdKnobPos; + q31_t ratioKnobPos; + q31_t attackKnobPos; + q31_t releaseKnobPos; }; diff --git a/src/deluge/model/global_effectable/global_effectable.cpp b/src/deluge/model/global_effectable/global_effectable.cpp index 5ca54c9cef..e7b5bb53af 100644 --- a/src/deluge/model/global_effectable/global_effectable.cpp +++ b/src/deluge/model/global_effectable/global_effectable.cpp @@ -311,12 +311,12 @@ int32_t GlobalEffectable::getKnobPosForNonExistentParam(int32_t whichModEncoder, break; case CompParam::ATTACK: - current = AudioEngine::mastercompressor.getAttack(); + current = AudioEngine::mastercompressor.getAttack() >> 24; break; case CompParam::RELEASE: - current = AudioEngine::mastercompressor.getRelease(); + current = AudioEngine::mastercompressor.getRelease() >> 24; break; } @@ -356,23 +356,21 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse break; case CompParam::ATTACK: - current = AudioEngine::mastercompressor.getAttack() - 64; + current = (AudioEngine::mastercompressor.getAttack() >> 24) - 64; current += offset; - current = std::clamp(current, -63, 64); + current = std::clamp(current, -64, 64); ledLevel = (64 + current); - displayLevel = ((ledLevel)*kMaxMenuValue) / 128; - AudioEngine::mastercompressor.setAttack(current + 64); + displayLevel = AudioEngine::mastercompressor.setAttack(lshiftAndSaturate<24>(current + 64)); break; case CompParam::RELEASE: - current = AudioEngine::mastercompressor.getRelease() - 64; + current = (AudioEngine::mastercompressor.getRelease() >> 24) - 64; current += offset; - current = std::clamp(current, -63, 64); + current = std::clamp(current, -64, 64); ledLevel = (64 + current); - displayLevel = ((ledLevel)*kMaxMenuValue) / 128; - AudioEngine::mastercompressor.setRelease(current + 64); + displayLevel = AudioEngine::mastercompressor.setRelease(lshiftAndSaturate<24>(current + 64)); break; } indicator_leds::setKnobIndicatorLevel(0, ledLevel); diff --git a/src/deluge/model/song/song.cpp b/src/deluge/model/song/song.cpp index cadbb6e389..96614013ca 100644 --- a/src/deluge/model/song/song.cpp +++ b/src/deluge/model/song/song.cpp @@ -136,8 +136,8 @@ Song::Song() : backedUpParamManagers(sizeof(BackedUpParamManager)) { reverbCompressorShape = -601295438; reverbCompressorSync = SYNC_LEVEL_8TH; - masterCompressorAttack = 10; - masterCompressorRelease = 100; + masterCompressorAttack = 10 << 24; + masterCompressorRelease = 20 << 24; masterCompressorThresh = 0; masterCompressorRatio = 0; AudioEngine::mastercompressor.gainReduction = 0.0; From 74b4b2f76af87e28009cca7150f73509e1efbf70 Mon Sep 17 00:00:00 2001 From: m-m-adams Date: Wed, 8 Nov 2023 22:35:17 -0500 Subject: [PATCH 4/9] move the falling behind warning back to println but comment out --- src/deluge/processing/engines/audio_engine.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/deluge/processing/engines/audio_engine.cpp b/src/deluge/processing/engines/audio_engine.cpp index eea303804f..f02d093d20 100644 --- a/src/deluge/processing/engines/audio_engine.cpp +++ b/src/deluge/processing/engines/audio_engine.cpp @@ -402,13 +402,15 @@ void routine() { #define MINSAMPLES 16 smoothedSamples = numSamples; - - if (numSamplesLastTime < numSamples) { - logAction("rendered "); - logAction(numSamplesLastTime); - logAction(" samples but output "); - logAction(numSamples); - } + //this is sometimes good for debugging but super spammy + //audiolog doesn't work because the render that notices the failure + //is one after the render with the problem + // if (numSamplesLastTime < numSamples) { + // Debug::println("rendered "); + // Debug::println(numSamplesLastTime); + // Debug::println(" samples but output "); + // Debug::println(numSamples); + // } // Consider direness and culling - before increasing the number of samples int32_t numSamplesLimit = 40; //storageManager.devVarC; From 10a313bec9fbc7b05b6d57f4d7381a3af7d8eb07 Mon Sep 17 00:00:00 2001 From: m-m-adams Date: Wed, 8 Nov 2023 22:42:07 -0500 Subject: [PATCH 5/9] revert changes to sidechain compressor class --- src/deluge/dsp/compressor/compressor.cpp | 126 ++--------------------- src/deluge/dsp/compressor/compressor.h | 2 +- 2 files changed, 10 insertions(+), 118 deletions(-) diff --git a/src/deluge/dsp/compressor/compressor.cpp b/src/deluge/dsp/compressor/compressor.cpp index c75669db05..6460a9bd89 100644 --- a/src/deluge/dsp/compressor/compressor.cpp +++ b/src/deluge/dsp/compressor/compressor.cpp @@ -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; @@ -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; } } @@ -133,23 +128,19 @@ 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; } } @@ -157,14 +148,10 @@ int32_t Compressor::render(uint16_t numSamples, int32_t shapeValue) { 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 @@ -204,110 +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 - getDecay4(8388608 - pos, 23))) << 1); - } - - else { // Off or hold - + else { // Off doOff: - lastValue = envelopeOffset; + lastValue = ONE_Q31; } return lastValue - ONE_Q31; diff --git a/src/deluge/dsp/compressor/compressor.h b/src/deluge/dsp/compressor/compressor.h index 78470afcc8..c48cc96040 100644 --- a/src/deluge/dsp/compressor/compressor.h +++ b/src/deluge/dsp/compressor/compressor.h @@ -26,6 +26,7 @@ class Compressor { public: Compressor(); void cloneFrom(Compressor* other); + EnvelopeStage status; uint32_t pos; int32_t lastValue; @@ -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); From 61e726d413be5e6d56617dddf9d82c8f6f54bed2 Mon Sep 17 00:00:00 2001 From: m-m-adams Date: Wed, 8 Nov 2023 23:50:46 -0500 Subject: [PATCH 6/9] weight moving average by num samples --- src/deluge/dsp/master_compressor/master_compressor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/deluge/dsp/master_compressor/master_compressor.cpp b/src/deluge/dsp/master_compressor/master_compressor.cpp index 737e1ea3a2..f7ebce6570 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.cpp +++ b/src/deluge/dsp/master_compressor/master_compressor.cpp @@ -94,7 +94,7 @@ 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(-(reduction)*9 * 4, 0, 127); + gainReduction = std::clamp(-(reduction) * 9 * 4, 0, 127); //calc compression for next round (feedback compressor) rms = calc_rms(buffer, numSamples); } @@ -127,7 +127,9 @@ float MasterCompressor::calc_rms(StereoSample* buffer, uint16_t numSamples) { 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 - mean = (mean + lastMean) / 2; + //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(rms, 1.0f)); From 66630538711ec181ad873f8e051719ae0141b529 Mon Sep 17 00:00:00 2001 From: m-m-adams Date: Wed, 8 Nov 2023 23:51:00 -0500 Subject: [PATCH 7/9] display ratio --- src/deluge/dsp/master_compressor/master_compressor.h | 6 +++--- src/deluge/model/global_effectable/global_effectable.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/deluge/dsp/master_compressor/master_compressor.h b/src/deluge/dsp/master_compressor/master_compressor.h index 1a54d859c4..ee4aef7299 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.h +++ b/src/deluge/dsp/master_compressor/master_compressor.h @@ -56,14 +56,14 @@ class MasterCompressor { q31_t getThreshold() { return thresholdKnobPos; } void setThreshold(q31_t t) { thresholdKnobPos = t; - threshold = 1 - ((thresholdKnobPos >> 1) / ONE_Q31f); + threshold = 1 - 0.8 * (float(thresholdKnobPos) / ONE_Q31f); updateER(); } q31_t getRatio() { return ratioKnobPos; } - void setRatio(q31_t rat) { + q31_t setRatio(q31_t rat) { ratioKnobPos = rat; ratio = 0.5 + (float(ratioKnobPos) / ONE_Q31f) / 2; - updateER(); + return 1 / (1 - ratio); } void updateER(); diff --git a/src/deluge/model/global_effectable/global_effectable.cpp b/src/deluge/model/global_effectable/global_effectable.cpp index e7b5bb53af..b67aa29f05 100644 --- a/src/deluge/model/global_effectable/global_effectable.cpp +++ b/src/deluge/model/global_effectable/global_effectable.cpp @@ -352,7 +352,7 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse ledLevel = (64 + current); displayLevel = ((ledLevel)*kMaxMenuValue) / 128; - AudioEngine::mastercompressor.setRatio(lshiftAndSaturate<24>(current + 64)); + displayLevel = AudioEngine::mastercompressor.setRatio(lshiftAndSaturate<24>(current + 64)); break; case CompParam::ATTACK: From 70053909c6ffd23094b13552b2c8ca6076c36681 Mon Sep 17 00:00:00 2001 From: m-m-adams Date: Thu, 9 Nov 2023 01:00:56 -0500 Subject: [PATCH 8/9] dbt format --- src/deluge/dsp/master_compressor/master_compressor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deluge/dsp/master_compressor/master_compressor.cpp b/src/deluge/dsp/master_compressor/master_compressor.cpp index f7ebce6570..dd88713ece 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.cpp +++ b/src/deluge/dsp/master_compressor/master_compressor.cpp @@ -94,7 +94,7 @@ 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(-(reduction) * 9 * 4, 0, 127); + gainReduction = std::clamp(-(reduction)*9 * 4, 0, 127); //calc compression for next round (feedback compressor) rms = calc_rms(buffer, numSamples); } From 725bc0891f9300aa08691684a3dd061b18f40487 Mon Sep 17 00:00:00 2001 From: m-m-adams Date: Thu, 9 Nov 2023 10:43:02 -0500 Subject: [PATCH 9/9] initialize current --- src/deluge/model/global_effectable/global_effectable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deluge/model/global_effectable/global_effectable.cpp b/src/deluge/model/global_effectable/global_effectable.cpp index b67aa29f05..5f088471b6 100644 --- a/src/deluge/model/global_effectable/global_effectable.cpp +++ b/src/deluge/model/global_effectable/global_effectable.cpp @@ -295,7 +295,7 @@ bool GlobalEffectable::modEncoderButtonAction(uint8_t whichModEncoder, bool on, } int32_t GlobalEffectable::getKnobPosForNonExistentParam(int32_t whichModEncoder, ModelStackWithAutoParam* modelStack) { - int current; + int current = 0; if (*getModKnobMode() == 4) { //this is only reachable in comp editing mode, otherwise it's an existent param