commit 2f2fbadf99ae485258759ce656b96b35a63133a7
parent 484ce2ce29fec1d4572e1c7e5c4fc83d790ff245
Author: jatinchowdhury18 <[email protected]>
Date: Fri, 12 Nov 2021 15:52:00 +0000
Implement options for mid/side processing (#227)
* ms-implementation
* Tweaks to mid-side implementation
* after rebase from the main repo and trying to avoid clicks
* Tweaks and fixes to mid/side feature
* Bring submodules up-to-date
* Do +/- 3 dB normalization in mid/side mode
* Improve mid/side fading behaviour
* Apply clang-format
Co-authored-by: ChaitanyaPTank <[email protected]>
Co-authored-by: jatinchowdhury18 <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Diffstat:
7 files changed, 122 insertions(+), 6 deletions(-)
diff --git a/Plugin/Source/GUI/Assets/gui.xml b/Plugin/Source/GUI/Assets/gui.xml
@@ -47,6 +47,11 @@
<View display="tabbed" padding="0" background-color="FF31323A" lookAndFeel="MyLNF">
<View flex-direction="column" tab-color="" background-color="FF31323A"
padding="0" tab-caption="Gain">
+ <TextButton id="mid_side" tooltip="Toggles between left/right and mid/side processing modes" margin="5" padding="0"
+ parameter="mid_side" text="Mid/Side" width="100" height="35"
+ min-height="30" min-width="100" flex-align-self="center" max-height="40"
+ name="Mid/Side" lookAndFeel="LookAndFeel_V3" button-color="FF33343D"
+ button-on-color="FFB41717" button-off-text="FFFFFFFF" button-on-text="FFFFFFFF"/>
<Slider caption="Input Gain [dB]" parameter="ingain" class="Slider" name="Input Gain"
padding="0" margin="0" tooltip="Sets the input gain to the tape model in Decibels."/>
<Slider caption="Dry/Wet" parameter="drywet" class="Slider" tooltip="Sets dry/wet mix of the entire plugin."
diff --git a/Plugin/Source/GUI/Assets/gui_ios.xml b/Plugin/Source/GUI/Assets/gui_ios.xml
@@ -81,6 +81,11 @@
<View display="tabbed" padding="0" background-color="FF31323A" lookAndFeel="MyLNF">
<View flex-direction="column" tab-color="" background-color="FF31323A"
padding="0" tab-caption="Gain">
+ <TextButton id="mid_side" tooltip="Toggles between left/right and mid/side processing modes" margin="5" padding="0"
+ parameter="mid_side" text="Mid/Side" width="100" height="35"
+ min-height="30" min-width="100" flex-align-self="center" max-height="40"
+ name="Mid/Side" lookAndFeel="LookAndFeel_V3" button-color="FF33343D"
+ button-on-color="FFB41717" button-off-text="FFFFFFFF" button-on-text="FFFFFFFF"/>
<Slider caption="Input Gain [dB]" parameter="ingain" class="Slider" name="Input Gain"
padding="0" margin="0" tooltip="Sets the input gain to the tape model in Decibels."/>
<View margin="0" padding="0" flex-grow="0.1" background-color="00000000"/>
diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp
@@ -31,6 +31,7 @@ ChowtapeModelAudioProcessor::ChowtapeModelAudioProcessor()
: AudioProcessor (BusesProperties().withInput ("Input", juce::AudioChannelSet::stereo(), true).withOutput ("Output", juce::AudioChannelSet::stereo(), true)),
vts (*this, nullptr, Identifier ("Parameters"), createParameterLayout()),
inputFilters (vts),
+ midSideController (vts),
toneControl (vts),
compressionProcessor (vts),
hysteresis (vts, *this),
@@ -77,6 +78,7 @@ AudioProcessorValueTreeState::ParameterLayout ChowtapeModelAudioProcessor::creat
WowFlutterProcessor::createParameterLayout (params);
DegradeProcessor::createParameterLayout (params);
ChewProcessor::createParameterLayout (params);
+ MidSideProcessor::createParameterLayout (params);
MixGroupsController::createParameterLayout (params);
return { params.begin(), params.end() };
@@ -164,6 +166,7 @@ void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesP
inGain.prepareToPlay (sampleRate, samplesPerBlock);
inputFilters.prepareToPlay (sampleRate, samplesPerBlock);
+ midSideController.prepare (sampleRate);
toneControl.prepare (sampleRate);
compressionProcessor.prepare (sampleRate, samplesPerBlock);
hysteresis.prepareToPlay (sampleRate, samplesPerBlock);
@@ -246,6 +249,7 @@ void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi
scope->pushSamplesIO (buffer, TapeScope::AudioType::Input);
+ midSideController.processInput (buffer);
toneControl.processBlockIn (buffer);
compressionProcessor.processBlock (buffer);
hysteresis.processBlock (buffer, midiMessages);
@@ -257,6 +261,7 @@ void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi
latencyCompensation();
+ midSideController.processOutput (buffer);
inputFilters.processBlockMakeup (buffer);
outGain.processBlock (buffer, midiMessages);
dryWet.processBlock (dryBuffer, buffer);
@@ -302,13 +307,9 @@ AudioProcessorEditor* ChowtapeModelAudioProcessor::createEditor()
builder->registerFactory ("PowerButton", &PowerButtonItem::factory);
builder->registerFactory ("OversamplingMenu", &OversamplingMenu::factory);
- builder->registerFactory ("FlutterMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> {
- return std::make_unique<WowFlutterMenuItem> (b, node, "Flutter");
- });
+ builder->registerFactory ("FlutterMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> { return std::make_unique<WowFlutterMenuItem> (b, node, "Flutter"); });
- builder->registerFactory ("WowMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> {
- return std::make_unique<WowFlutterMenuItem> (b, node, "Wow");
- });
+ builder->registerFactory ("WowMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> { return std::make_unique<WowFlutterMenuItem> (b, node, "Wow"); });
builder->registerJUCELookAndFeels();
builder->registerLookAndFeel ("MyLNF", std::make_unique<MyLNF>());
diff --git a/Plugin/Source/PluginProcessor.h b/Plugin/Source/PluginProcessor.h
@@ -24,6 +24,7 @@
#include "Processors/Hysteresis/ToneControl.h"
#include "Processors/Input_Filters/InputFilters.h"
#include "Processors/Loss_Effects/LossFilter.h"
+#include "Processors/MidSide/MidSideProcessor.h"
#include "Processors/Timing_Effects/WowFlutterProcessor.h"
#include <JuceHeader.h>
@@ -91,6 +92,7 @@ private:
GainProcessor inGain;
InputFilters inputFilters;
+ MidSideProcessor midSideController;
ToneControl toneControl;
CompressionProcessor compressionProcessor;
HysteresisProcessor hysteresis;
diff --git a/Plugin/Source/Processors/CMakeLists.txt b/Plugin/Source/Processors/CMakeLists.txt
@@ -13,6 +13,7 @@ target_sources(CHOWTapeModel PRIVATE
Input_Filters/InputFilters.cpp
Loss_Effects/AzimuthProc.cpp
Loss_Effects/LossFilter.cpp
+ MidSide/MidSideProcessor.cpp
Timing_Effects/WowFlutterProcessor.cpp
Timing_Effects/FlutterProcess.cpp
Timing_Effects/WowProcess.cpp
diff --git a/Plugin/Source/Processors/MidSide/MidSideProcessor.cpp b/Plugin/Source/Processors/MidSide/MidSideProcessor.cpp
@@ -0,0 +1,76 @@
+#include "MidSideProcessor.h"
+
+MidSideProcessor::MidSideProcessor (AudioProcessorValueTreeState& vts)
+{
+ // set up parameter handle here
+ midSideParam = vts.getRawParameterValue ("mid_side");
+}
+
+void MidSideProcessor::createParameterLayout (std::vector<std::unique_ptr<RangedAudioParameter>>& params)
+{
+ // add parameters here
+ params.push_back (std::make_unique<AudioParameterBool> ("mid_side", "Mid/Side Mode", false));
+}
+
+void MidSideProcessor::prepare (double sampleRate)
+{
+ fadeSmooth.reset (sampleRate, 0.04);
+
+ curMS = *midSideParam == 1.0f;
+ prevMS = curMS;
+}
+
+void MidSideProcessor::processInput (AudioBuffer<float>& buffer)
+{
+ const auto numSamples = buffer.getNumSamples();
+
+ //mid - side encoding logic here
+ if (curMS && buffer.getNumChannels() != 1)
+ {
+ buffer.addFrom (0, 0, buffer, 1, 0, numSamples); // make channel 0 = left + right = mid
+ buffer.applyGain (1, 0, numSamples, 2.0f); // make channel 1 = 2 * right
+ buffer.addFrom (1, 0, buffer, 0, 0, numSamples, -1.0f); // make channel 1 = (2 * right) - (left + right) = right - left
+ buffer.applyGain (1, 0, numSamples, -1.0f); // make channel 1 = -1 * (right - left) = left - right = side
+
+ buffer.applyGain (Decibels::decibelsToGain (-3.0f)); // -3 dB Normalization
+ }
+}
+
+void MidSideProcessor::processOutput (AudioBuffer<float>& buffer)
+{
+ const auto numSamples = buffer.getNumSamples();
+
+ if (prevMS != (*midSideParam == 1.0f) && ! fadeSmooth.isSmoothing())
+ {
+ fadeSmooth.setCurrentAndTargetValue (1.0f);
+ fadeSmooth.setTargetValue (0.0f);
+ }
+
+ //mid - side decoding logic here
+ if (curMS && buffer.getNumChannels() != 1)
+ {
+ buffer.applyGain (Decibels::decibelsToGain (3.0f)); // undo -3 dB Normalization
+
+ buffer.applyGain (1, 0, numSamples, -1.0f); // channel 1 = (L - R) * -1 = R - L
+ buffer.addFrom (0, 0, buffer, 1, 0, numSamples, -1.0f); // channel 0 = (L + R) - (R - L) = 2L
+ buffer.applyGain (0, 0, numSamples, 0.5f); // channel 0: 0.5 * (2L) = L
+ buffer.addFrom (1, 0, buffer, 0, 0, numSamples); // channel 1 = (R - L) + L = R
+ }
+
+ if (fadeSmooth.isSmoothing())
+ {
+ float startGain = fadeSmooth.getCurrentValue();
+ float endGain = fadeSmooth.skip (numSamples);
+
+ buffer.applyGainRamp (0, numSamples, startGain, endGain);
+
+ if (endGain == 0.0f)
+ {
+ fadeSmooth.setTargetValue (1.0f);
+
+ // reset curMS at the "bottom" of the fade
+ curMS = *midSideParam == 1.0f;
+ prevMS = curMS;
+ }
+ }
+}
diff --git a/Plugin/Source/Processors/MidSide/MidSideProcessor.h b/Plugin/Source/Processors/MidSide/MidSideProcessor.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <JuceHeader.h>
+
+class MidSideProcessor
+{
+public:
+ MidSideProcessor (AudioProcessorValueTreeState& vts);
+
+ static void createParameterLayout (std::vector<std::unique_ptr<RangedAudioParameter>>& params);
+
+ void prepare (double sampleRate);
+
+ void processInput (AudioBuffer<float>& buffer);
+ void processOutput (AudioBuffer<float>& buffer);
+
+private:
+ std::atomic<float>* midSideParam = nullptr; // parameter handle
+
+ bool curMS = false;
+ bool prevMS = false;
+
+ SmoothedValue<float, ValueSmoothingTypes::Linear> fadeSmooth;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidSideProcessor)
+};