From 3d4abc1f94eacfdd029424899f7458fe7499b40c Mon Sep 17 00:00:00 2001 From: Jose Diaz Date: Sat, 31 Aug 2024 23:43:34 +0200 Subject: [PATCH] Get sidechain signal into Valentine use oversampling, doesn't work hack --- libs/tote_bag/dsp/Compressor.cpp | 15 +++++--- libs/tote_bag/dsp/Compressor.h | 15 ++++---- src/PluginProcessor.cpp | 66 ++++++++++++++++++++++++-------- src/PluginProcessor.h | 5 ++- 4 files changed, 71 insertions(+), 30 deletions(-) diff --git a/libs/tote_bag/dsp/Compressor.cpp b/libs/tote_bag/dsp/Compressor.cpp index e6187491..db6c7304 100644 --- a/libs/tote_bag/dsp/Compressor.cpp +++ b/libs/tote_bag/dsp/Compressor.cpp @@ -62,7 +62,8 @@ void Compressor::setOversampleMultiplier (const int o) overSampleMultiplier = o - 1; } -inline void Compressor::makeMonoSidechain (const juce::dsp::AudioBlock& inAudio, juce::AudioBuffer& scSignal) +inline void Compressor::makeMonoSidechain (const juce::dsp::AudioBlock& inAudio, + juce::AudioBuffer& scSignal) { scSignal.clear(); auto* scWritePointer = scSignal.getWritePointer (0); @@ -101,7 +102,9 @@ inline float Compressor::calculateGain (float inputSample) else if (abs (doubleOvershoot) <= W) { - cvOutput = inputSample + ((1.0f / R - 1.0f) * powf (overshoot + W / 2.0f, 2)) / (2.0f * W); + cvOutput = + inputSample + + ((1.0f / R - 1.0f) * powf (overshoot + W / 2.0f, 2)) / (2.0f * W); } else { @@ -121,9 +124,10 @@ inline float Compressor::getMakeupGain() return -threshold.get() * (1.0f / ratio.get() - 1.0f) / 2.0f; } -void Compressor::process (juce::dsp::AudioBlock& inAudio) +void Compressor::process (juce::dsp::AudioBlock& inAudio, + juce::dsp::AudioBlock& sidechainInput) { - makeMonoSidechain (inAudio, analysisSignal); + makeMonoSidechain (sidechainInput, analysisSignal); auto numSamples = inAudio.getNumSamples(); auto numChannels = inAudio.getNumChannels(); @@ -132,7 +136,8 @@ void Compressor::process (juce::dsp::AudioBlock& inAudio) { auto controlVoltage = juce::Decibels::gainToDecibels (abs (sidechain[sample])); // TODO: level detector methods should be float or templated - controlVoltage = static_cast (levelDetector.processSampleDecoupledBranched (controlVoltage)); + controlVoltage = static_cast ( + levelDetector.processSampleDecoupledBranched (controlVoltage)); controlVoltage = calculateGain (controlVoltage); controlVoltage = juce::Decibels::decibelsToGain (controlVoltage); diff --git a/libs/tote_bag/dsp/Compressor.h b/libs/tote_bag/dsp/Compressor.h index 6c765016..8c953bf6 100644 --- a/libs/tote_bag/dsp/Compressor.h +++ b/libs/tote_bag/dsp/Compressor.h @@ -29,13 +29,15 @@ class Compressor float inThreshold, float inKnee); - void makeMonoSidechain (const juce::dsp::AudioBlock& inAudio, juce::AudioBuffer& scSignal); + void makeMonoSidechain (const juce::dsp::AudioBlock& inAudio, + juce::AudioBuffer& scSignal); void makeKneeCoeffs(); float calculateGain (float analysisSample); - void process (juce::dsp::AudioBlock& inAudio); + void process (juce::dsp::AudioBlock& inAudio, + juce::dsp::AudioBlock& sidechainInput); float getMakeupGain(); @@ -51,16 +53,13 @@ class Compressor private: juce::WeakReference meterSource; - juce::Atomic ratio { -1.0f }, - knee { -1.0f }, - msAttack { -1.0f }, - msRelease { -1.0f }, - threshold { -1.0 }; + juce::Atomic ratio {-1.0f}, knee {-1.0f}, msAttack {-1.0f}, msRelease {-1.0f}, + threshold {-1.0}; EnvelopeDetector levelDetector; juce::AudioBuffer analysisSignal; - int overSampleMultiplier { 1 }; + int overSampleMultiplier {1}; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Compressor) }; diff --git a/src/PluginProcessor.cpp b/src/PluginProcessor.cpp index 1583d1a1..1395aeb5 100644 --- a/src/PluginProcessor.cpp +++ b/src/PluginProcessor.cpp @@ -75,6 +75,8 @@ ValentineAudioProcessor::ValentineAudioProcessor() #if !JucePlugin_IsMidiEffect #if !JucePlugin_IsSynth .withInput ("Input", juce::AudioChannelSet::stereo(), true) + .withInput ("Sidechain", juce::AudioChannelSet::stereo(), false) + #endif .withOutput ("Output", juce::AudioChannelSet::stereo(), true) #endif @@ -83,6 +85,9 @@ ValentineAudioProcessor::ValentineAudioProcessor() , presetManager (this) , processedDelayLine (32) , cleanDelayLine (32) + , sidechainOversampler (2, + detail::kOversampleFactor, + Oversampling::filterHalfBandPolyphaseIIR) #endif { initializeDSP(); @@ -253,6 +258,8 @@ void ValentineAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBl oversampler->reset(); oversampler->initProcessing (static_cast (samplesPerBlock)); + sidechainOversampler.reset(); + sidechainOversampler.initProcessing (static_cast (samplesPerBlock)); saturator->reset (sampleRate); boundedSaturator->reset (sampleRate); @@ -264,7 +271,7 @@ void ValentineAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBl const auto rmsWindow = juce::roundToInt (detail::kRmsTime * 0.001f * sampleRate / samplesPerBlock); - inputMeterSource.resize (getTotalNumInputChannels(), rmsWindow); + inputMeterSource.resize (getMainBusNumInputChannels(), rmsWindow); grMeterSource.resize (1, rmsWindow); ffCompressor->setMeterSource (&grMeterSource); @@ -306,8 +313,12 @@ void ValentineAudioProcessor::processBlock (juce::AudioBuffer& buffer, juce::MidiBuffer&) { juce::ScopedNoDenormals noDenormals; - auto totalNumInputChannels = getTotalNumInputChannels(); + const auto numMainBusInputChannels = getMainBusNumInputChannels(); auto totalNumOutputChannels = getTotalNumOutputChannels(); + + auto mainBusInputOutput = getBusBuffer (buffer, true, 0); + auto sidechainInput = getBusBuffer (buffer, true, 1); + auto bufferSize = buffer.getNumSamples(); auto currentSamplesPerBlock = bufferSize; @@ -321,19 +332,19 @@ void ValentineAudioProcessor::processBlock (juce::AudioBuffer& buffer, // oversampling latency. Copying into the process buffer after this, then, would undo the // purpose of the delay: maintaining phase coherence between processed and unprocessed // signals. - processBuffer.setSize (totalNumOutputChannels, + processBuffer.setSize (numMainBusInputChannels, currentSamplesPerBlock, true, true, true); - for (auto channel = 0; channel < totalNumInputChannels; ++channel) + for (auto channel = 0; channel < numMainBusInputChannels; ++channel) processBuffer.copyFrom (channel, 0, - buffer.getReadPointer (channel), - buffer.getNumSamples()); + mainBusInputOutput.getReadPointer (channel), + mainBusInputOutput.getNumSamples()); - prepareInputBuffer (buffer, - totalNumInputChannels, + prepareInputBuffer (mainBusInputOutput, + numMainBusInputChannels, totalNumOutputChannels, currentSamplesPerBlock); @@ -342,7 +353,7 @@ void ValentineAudioProcessor::processBlock (juce::AudioBuffer& buffer, return; } - inputMeterSource.measureBlock (buffer); + inputMeterSource.measureBlock (mainBusInputOutput); // "Downsample" and Bitcrush processing if (crushOn.get()) @@ -357,8 +368,22 @@ void ValentineAudioProcessor::processBlock (juce::AudioBuffer& buffer, } auto g = compressValue.get(); - for (int i = 0; i < totalNumOutputChannels; ++i) - processBuffer.applyGainRamp (i, 0, bufferSize, currentGain, g); + + if (sidechainInput.getNumChannels() > 0) + { + for (int i = 0; i < totalNumOutputChannels; ++i) + { + sidechainInput.applyGainRamp (i, 0, bufferSize, currentGain, g); + } + } + else + { + for (int i = 0; i < totalNumOutputChannels; ++i) + processBuffer.applyGainRamp (i, 0, bufferSize, currentGain, g); + } + + // for (int i = 0; i < totalNumOutputChannels; ++i) + // processBuffer.applyGainRamp (i, 0, bufferSize, currentGain, g); currentGain = g; // Upsample then do non-linear processing @@ -366,7 +391,15 @@ void ValentineAudioProcessor::processBlock (juce::AudioBuffer& buffer, juce::dsp::AudioBlock highSampleRateBlock = oversampler->processSamplesUp (processBlock); - ffCompressor->process (highSampleRateBlock); + if (sidechainInput.getNumChannels() > 0) + { + juce::dsp::AudioBlock highSampleRateSideChain = + sidechainOversampler.processSamplesUp (sidechainInput); + ffCompressor->process (highSampleRateBlock, highSampleRateSideChain); + } + { + ffCompressor->process (highSampleRateBlock, highSampleRateBlock); + } if (saturateOn.get()) { @@ -426,13 +459,16 @@ void ValentineAudioProcessor::processBlock (juce::AudioBuffer& buffer, for (int i = 0; i < totalNumOutputChannels; ++i) { auto processed = processBuffer.getSample (i, j); - auto unprocessed = buffer.getSample (i, j); + auto unprocessed = mainBusInputOutput.getSample (i, j); - buffer.setSample (i, j, mix * processed + (1.0f - currentMix) * unprocessed); + mainBusInputOutput.setSample (i, + j, + mix * processed + + (1.0f - currentMix) * unprocessed); } } - outputMeterSource.measureBlock (buffer); + outputMeterSource.measureBlock (mainBusInputOutput); } void ValentineAudioProcessor::processBlockBypassed (juce::AudioBuffer& buffer, diff --git a/src/PluginProcessor.h b/src/PluginProcessor.h index c3475e07..6593016d 100644 --- a/src/PluginProcessor.h +++ b/src/PluginProcessor.h @@ -199,8 +199,8 @@ class ValentineAudioProcessor : public juce::AudioProcessor, FFCompParameterDefaults[static_cast (VParameter::outputClipEnable)] > 0.5f}; bool clipOnState = clipOn.get(); - juce::Atomic saturateOn = FFCompParameterDefaults[static_cast (VParameter::saturateEnable)] - > 0.5f; + juce::Atomic saturateOn = + FFCompParameterDefaults[static_cast (VParameter::saturateEnable)] > 0.5f; bool saturateOnState = saturateOn.get(); // This is used for, yes you guessed it, processing @@ -209,6 +209,7 @@ class ValentineAudioProcessor : public juce::AudioProcessor, // DSP objects std::unique_ptr> dryWetFilter; std::unique_ptr oversampler; + Oversampling sidechainOversampler; std::unique_ptr ffCompressor; std::unique_ptr saturator; std::unique_ptr boundedSaturator;