AnalogTapeModel

Physical modelling signal processing for analog tape recording
Log | Files | Refs | Submodules | README | LICENSE

MidSideProcessor.cpp (4729B)


      1 #include "MidSideProcessor.h"
      2 
      3 namespace
      4 {
      5 constexpr auto balanceGainDB = 6.0f;
      6 }
      7 
      8 MidSideProcessor::MidSideProcessor (AudioProcessorValueTreeState& vts)
      9 {
     10     // set up parameter handle here
     11     midSideParam = vts.getRawParameterValue ("mid_side");
     12     chowdsp::ParamUtils::loadParameterPointer (balanceParam, vts, "stereo_balance");
     13     makeupParam = vts.getRawParameterValue ("stereo_makeup");
     14 }
     15 
     16 void MidSideProcessor::createParameterLayout (chowdsp::Parameters& params)
     17 {
     18     using namespace chowdsp::ParamUtils;
     19     emplace_param<chowdsp::BoolParameter> (params, "mid_side", "Mid/Side Mode", false);
     20     emplace_param<chowdsp::FloatParameter> (params, "stereo_balance", "Stereo Balance", NormalisableRange { -1.0f, 1.0f }, 0.0f, &percentValToString, &stringToPercentVal);
     21     emplace_param<chowdsp::BoolParameter> (params, "stereo_makeup", "Stereo Makeup", true);
     22 }
     23 
     24 void MidSideProcessor::prepare (double sampleRate, int samplesPerBlock)
     25 {
     26     fadeSmooth.reset (sampleRate, 0.04);
     27 
     28     for (auto& inGain : inBalanceGain)
     29     {
     30         inGain.prepare ({ sampleRate, (uint32) samplesPerBlock, 2 });
     31         inGain.setRampDurationSeconds (0.05);
     32     }
     33 
     34     for (auto& outGain : outBalanceGain)
     35     {
     36         outGain.prepare ({ sampleRate, (uint32) samplesPerBlock, 2 });
     37         outGain.setRampDurationSeconds (0.05);
     38     }
     39 
     40     curMS = *midSideParam == 1.0f;
     41     prevMS = curMS;
     42 }
     43 
     44 void MidSideProcessor::processInput (AudioBuffer<float>& buffer)
     45 {
     46     if (buffer.getNumChannels() != 2) // needs to be stereo!
     47         return;
     48 
     49     //mid - side encoding logic here
     50     const auto numSamples = buffer.getNumSamples();
     51     if (curMS)
     52     {
     53         buffer.addFrom (0, 0, buffer, 1, 0, numSamples); // make channel 0 = left + right = mid
     54         buffer.applyGain (1, 0, numSamples, 2.0f); // make channel 1 = 2 * right
     55         buffer.addFrom (1, 0, buffer, 0, 0, numSamples, -1.0f); // make channel 1 = (2 * right) - (left + right) = right - left
     56         buffer.applyGain (1, 0, numSamples, -1.0f); // make channel 1 = -1 * (right - left) = left - right = side
     57 
     58         buffer.applyGain (Decibels::decibelsToGain (-3.0f)); // -3 dB Normalization
     59     }
     60 
     61     // balance processing
     62     const auto curBalance = balanceParam->getCurrentValue();
     63     auto&& stereoBlock = dsp::AudioBlock<float> { buffer };
     64     auto&& leftBlock = stereoBlock.getSingleChannelBlock (0);
     65     auto&& rightBlock = stereoBlock.getSingleChannelBlock (1);
     66 
     67     inBalanceGain[0].setGainDecibels (curBalance * balanceGainDB);
     68     inBalanceGain[0].process (dsp::ProcessContextReplacing<float> { leftBlock });
     69 
     70     inBalanceGain[1].setGainDecibels (curBalance * -balanceGainDB);
     71     inBalanceGain[1].process (dsp::ProcessContextReplacing<float> { rightBlock });
     72 }
     73 
     74 void MidSideProcessor::processOutput (AudioBuffer<float>& buffer)
     75 {
     76     if (buffer.getNumChannels() != 2) // needs to be stereo!
     77         return;
     78 
     79     if (prevMS != (*midSideParam == 1.0f) && ! fadeSmooth.isSmoothing())
     80     {
     81         fadeSmooth.setCurrentAndTargetValue (1.0f);
     82         fadeSmooth.setTargetValue (0.0f);
     83     }
     84 
     85     // inverse balance processing
     86     if (*makeupParam == 1.0f)
     87     {
     88         const auto curBalance = balanceParam->getCurrentValue();
     89         auto&& stereoBlock = dsp::AudioBlock<float> { buffer };
     90         auto&& leftBlock = stereoBlock.getSingleChannelBlock (0);
     91         auto&& rightBlock = stereoBlock.getSingleChannelBlock (1);
     92 
     93         outBalanceGain[0].setGainDecibels (curBalance * -balanceGainDB);
     94         outBalanceGain[0].process (dsp::ProcessContextReplacing<float> { leftBlock });
     95 
     96         outBalanceGain[1].setGainDecibels (curBalance * balanceGainDB);
     97         outBalanceGain[1].process (dsp::ProcessContextReplacing<float> { rightBlock });
     98     }
     99 
    100     //mid - side decoding logic here
    101     const auto numSamples = buffer.getNumSamples();
    102     if (curMS)
    103     {
    104         buffer.applyGain (Decibels::decibelsToGain (3.0f)); // undo -3 dB Normalization
    105 
    106         buffer.applyGain (1, 0, numSamples, -1.0f); // channel 1 = (L - R) * -1 = R - L
    107         buffer.addFrom (0, 0, buffer, 1, 0, numSamples, -1.0f); // channel 0 = (L + R) - (R - L) = 2L
    108         buffer.applyGain (0, 0, numSamples, 0.5f); // channel 0: 0.5 * (2L) = L
    109         buffer.addFrom (1, 0, buffer, 0, 0, numSamples); // channel 1 = (R - L) + L = R
    110     }
    111 
    112     if (fadeSmooth.isSmoothing())
    113     {
    114         float startGain = fadeSmooth.getCurrentValue();
    115         float endGain = fadeSmooth.skip (numSamples);
    116 
    117         buffer.applyGainRamp (0, numSamples, startGain, endGain);
    118 
    119         if (endGain == 0.0f)
    120         {
    121             fadeSmooth.setTargetValue (1.0f);
    122 
    123             // reset curMS at the "bottom" of the fade
    124             curMS = *midSideParam == 1.0f;
    125             prevMS = curMS;
    126         }
    127     }
    128 }