CompressionProcessor.cpp (4510B)
1 #include "CompressionProcessor.h" 2 3 CompressionProcessor::CompressionProcessor (AudioProcessorValueTreeState& vts) 4 { 5 using namespace chowdsp::ParamUtils; 6 onOff = vts.getRawParameterValue ("comp_onoff"); 7 loadParameterPointer (amountParam, vts, "comp_amt"); 8 loadParameterPointer (attackParam, vts, "comp_attack"); 9 loadParameterPointer (releaseParam, vts, "comp_release"); 10 } 11 12 void CompressionProcessor::createParameterLayout (chowdsp::Parameters& params) 13 { 14 using namespace chowdsp::ParamUtils; 15 emplace_param<chowdsp::BoolParameter> (params, "comp_onoff", "Compression On/Off", false); 16 createGainDBParameter (params, "comp_amt", "Compression Amount", 0.0f, 9.0f, 0.0f, 3.0f); 17 createTimeMsParameter (params, "comp_attack", "Compression Attack", createNormalisableRange (0.1f, 50.0f, 10.0f), 5.0f); 18 createTimeMsParameter (params, "comp_release", "Compression Release", createNormalisableRange (10.0f, 1000.0f, 100.0f), 200.0f); 19 } 20 21 void CompressionProcessor::prepare (double sr, int samplesPerBlock, int numChannels) 22 { 23 oversample = std::make_unique<dsp::Oversampling<float>> (numChannels, 1, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, true); 24 oversample->initProcessing ((size_t) samplesPerBlock); 25 auto osFactor = oversample->getOversamplingFactor(); 26 bypass.prepare (samplesPerBlock, numChannels, bypass.toBool (onOff)); 27 28 slewLimiter.clear(); 29 dbPlusSmooth.clear(); 30 for (int ch = 0; ch < numChannels; ++ch) 31 { 32 slewLimiter.emplace_back(); 33 dbPlusSmooth.emplace_back(); 34 35 slewLimiter[ch].prepare ({ sr, (uint32) samplesPerBlock, 1 }); 36 dbPlusSmooth[ch].reset (sr, 0.05); 37 } 38 39 xDBVec.resize (osFactor * (size_t) samplesPerBlock, 0.0f); 40 compGainVec.resize (osFactor * (size_t) samplesPerBlock, 0.0f); 41 } 42 43 template <typename T> 44 inline T compressionDB (const T& xDB, float dbPlus) 45 { 46 using namespace chowdsp::SIMDUtils; 47 CHOWDSP_USING_XSIMD_STD (log); 48 49 if (dbPlus <= 0.0f) 50 return (T) dbPlus; 51 52 auto window = 2.0f * dbPlus; 53 auto belowWin = xDB < -window; 54 return select (belowWin, (T) dbPlus, log (xDB + window + 1.0f) - dbPlus - xDB); 55 } 56 57 void CompressionProcessor::processBlock (AudioBuffer<float>& buffer) 58 { 59 if (! bypass.processBlockIn (buffer, bypass.toBool (onOff))) 60 return; 61 62 dsp::AudioBlock<float> block (buffer); 63 auto osBlock = oversample->processSamplesUp (block); 64 65 const auto numSamples = (int) osBlock.getNumSamples(); 66 for (int ch = 0; ch < buffer.getNumChannels(); ++ch) 67 { 68 dbPlusSmooth[ch].setTargetValue (amountParam->getCurrentValue()); 69 70 auto* x = osBlock.getChannelPointer ((size_t) ch); 71 FloatVectorOperations::copy (xDBVec.data(), x, numSamples); 72 FloatVectorOperations::abs (xDBVec.data(), xDBVec.data(), numSamples); 73 74 constexpr auto inc = xsimd::batch<float>::size; 75 size_t n = 0; 76 for (; n < (size_t) numSamples; n += inc) 77 { 78 auto xDB = xsimd::load_aligned (&xDBVec[n]); 79 80 xDB = chowdsp::SIMDUtils::gainToDecibels (xDB); 81 auto compDB = compressionDB (xDB, dbPlusSmooth[ch].skip ((int) inc)); 82 auto compGain = chowdsp::SIMDUtils::decibelsToGain (compDB); 83 84 xsimd::store_aligned (&xDBVec[n], xDB); 85 xsimd::store_aligned (&compGainVec[n], compGain); 86 } 87 88 // remaining samples that can't be vectorized 89 for (; n < (size_t) numSamples; ++n) 90 { 91 xDBVec[n] = Decibels::gainToDecibels (xDBVec[n]); 92 auto compDB = compressionDB (xDBVec[n], dbPlusSmooth[ch].getNextValue()); 93 compGainVec[n] = Decibels::decibelsToGain (compDB); 94 } 95 96 // since the slew will be applied to the gain, we need to reverse the attack and release parameters! 97 slewLimiter[ch].setParameters (releaseParam->getCurrentValue(), attackParam->getCurrentValue()); 98 for (size_t k = 0; k < (size_t) numSamples; ++k) 99 compGainVec[k] = jmin (compGainVec[k], slewLimiter[ch].processSample (compGainVec[k])); 100 101 FloatVectorOperations::multiply (x, compGainVec.data(), numSamples); 102 } 103 104 oversample->processSamplesDown (block); 105 106 bypass.processBlockOut (buffer, bypass.toBool (onOff)); 107 } 108 109 float CompressionProcessor::getLatencySamples() const noexcept 110 { 111 if (oversample == nullptr) 112 return 0.0f; 113 114 return onOff->load() == 1.0f ? oversample->getLatencyInSamples() // on 115 : 0.0f; // off 116 }