AnalogTapeModel

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

WowFlutterProcessor.cpp (5362B)


      1 #include "WowFlutterProcessor.h"
      2 #include "../../GUI/Visualizers/LightMeter.h"
      3 
      4 WowFlutterProcessor::WowFlutterProcessor (AudioProcessorValueTreeState& vts)
      5 {
      6     using namespace chowdsp::ParamUtils;
      7     loadParameterPointer (flutterRate, vts, "rate");
      8     loadParameterPointer (flutterDepth, vts, "depth");
      9     loadParameterPointer (wowRate, vts, "wow_rate");
     10     loadParameterPointer (wowDepth, vts, "wow_depth");
     11     loadParameterPointer (wowVariance, vts, "wow_var");
     12     loadParameterPointer (wowDrift, vts, "wow_drift");
     13     flutterOnOff = vts.getRawParameterValue ("flutter_onoff");
     14 }
     15 
     16 void WowFlutterProcessor::initialisePlots (foleys::MagicGUIState& magicState)
     17 {
     18     wowPlot = magicState.createAndAddObject<LightMeter> ("wow");
     19     magicState.addBackgroundProcessing (wowPlot);
     20 
     21     flutterPlot = magicState.createAndAddObject<LightMeter> ("flutter");
     22     magicState.addBackgroundProcessing (flutterPlot);
     23 }
     24 
     25 void WowFlutterProcessor::createParameterLayout (chowdsp::Parameters& params)
     26 {
     27     using namespace chowdsp::ParamUtils;
     28     emplace_param<chowdsp::BoolParameter> (params, "flutter_onoff", "Wow/Flutter On/Off", true);
     29     emplace_param<chowdsp::FloatParameter> (params, "rate", "Flutter Rate", NormalisableRange { 0.0f, 1.0f }, 0.3f, &floatValToString, &stringToFloatVal);
     30     emplace_param<chowdsp::FloatParameter> (params, "depth", "Flutter Depth", NormalisableRange { 0.0f, 1.0f }, 0.0f, &floatValToString, &stringToFloatVal);
     31     emplace_param<chowdsp::FloatParameter> (params, "wow_rate", "Wow Rate", NormalisableRange { 0.0f, 1.0f }, 0.25f, &floatValToString, &stringToFloatVal);
     32     emplace_param<chowdsp::FloatParameter> (params, "wow_depth", "Wow Depth", NormalisableRange { 0.0f, 1.0f }, 0.0f, &floatValToString, &stringToFloatVal);
     33     emplace_param<chowdsp::FloatParameter> (params, "wow_var", "Wow Variance", NormalisableRange { 0.0f, 1.0f }, 0.0f, &floatValToString, &stringToFloatVal);
     34     emplace_param<chowdsp::FloatParameter> (params, "wow_drift", "Wow Drift", NormalisableRange { 0.0f, 1.0f }, 0.0f, &floatValToString, &stringToFloatVal);
     35 }
     36 
     37 void WowFlutterProcessor::prepareToPlay (double sampleRate, int samplesPerBlock, int numChannels)
     38 {
     39     fs = (float) sampleRate;
     40 
     41     bypass.prepare (samplesPerBlock, numChannels, bypass.toBool (flutterOnOff));
     42     wowProcessor.prepare (sampleRate, samplesPerBlock, numChannels);
     43     flutterProcessor.prepare (sampleRate, samplesPerBlock, numChannels);
     44 
     45     delay.prepare ({ sampleRate, (uint32) samplesPerBlock, (uint32) numChannels });
     46     delay.setDelay (0.0f);
     47 
     48     dcBlocker.resize ((size_t) numChannels);
     49     for (auto& filt : dcBlocker)
     50         filt.prepare (sampleRate, 15.0f);
     51 
     52     wowPlot->prepareToPlay (sampleRate, samplesPerBlock);
     53     flutterPlot->prepareToPlay (sampleRate, samplesPerBlock);
     54 }
     55 
     56 void WowFlutterProcessor::processBlock (AudioBuffer<float>& buffer)
     57 {
     58     ScopedNoDenormals noDenormals;
     59 
     60     const auto numChannels = buffer.getNumChannels();
     61     const auto numSamples = buffer.getNumSamples();
     62 
     63     auto curDepthWow = powf (*wowDepth, 3.0f);
     64     auto wowFreq = powf (4.5, *wowRate) - 1.0f;
     65     wowProcessor.prepareBlock (curDepthWow, wowFreq, wowVariance->getCurrentValue(), wowDrift->getCurrentValue(), numSamples, numChannels);
     66 
     67     auto curDepthFlutter = powf (powf (*flutterDepth, 3.0f) * 81.0f / 625.0f, 0.5f);
     68     auto flutterFreq = 0.1f * powf (1000.0f, *flutterRate);
     69     flutterProcessor.prepareBlock (curDepthFlutter, flutterFreq, numSamples, numChannels);
     70 
     71     bool shouldTurnOff = ! bypass.toBool (flutterOnOff) || (wowProcessor.shouldTurnOff() && flutterProcessor.shouldTurnOff());
     72     if (bypass.processBlockIn (buffer, ! shouldTurnOff))
     73     {
     74         processWetBuffer (buffer);
     75 
     76         for (int ch = 0; ch < buffer.getNumChannels(); ++ch)
     77             dcBlocker[ch].processBlock (buffer.getWritePointer (ch), buffer.getNumSamples());
     78 
     79         bypass.processBlockOut (buffer, ! shouldTurnOff);
     80     }
     81     else
     82     {
     83         processBypassed (buffer);
     84     }
     85 
     86     wowProcessor.plotBuffer (wowPlot);
     87     flutterProcessor.plotBuffer (flutterPlot);
     88 }
     89 
     90 void WowFlutterProcessor::processWetBuffer (AudioBuffer<float>& buffer)
     91 {
     92     for (int ch = 0; ch < buffer.getNumChannels(); ++ch)
     93     {
     94         auto* x = buffer.getWritePointer (ch);
     95         for (int n = 0; n < buffer.getNumSamples(); ++n)
     96         {
     97             auto [wowLFO, wowOffset] = wowProcessor.getLFO (n, ch);
     98             auto [flutterLFO, flutterOffset] = flutterProcessor.getLFO (n, ch);
     99 
    100             auto newLength = (wowLFO + flutterLFO + flutterOffset + wowOffset) * fs / 1000.0f;
    101             newLength = jlimit (0.0f, (float) HISTORY_SIZE, newLength);
    102 
    103             delay.setDelay (newLength);
    104             delay.pushSample (ch, x[n]);
    105             x[n] = delay.popSample (ch);
    106         }
    107 
    108         wowProcessor.boundPhase (ch);
    109         flutterProcessor.boundPhase (ch);
    110     }
    111 }
    112 
    113 void WowFlutterProcessor::processBypassed (const AudioBuffer<float>& buffer)
    114 {
    115     for (int ch = 0; ch < buffer.getNumChannels(); ++ch)
    116     {
    117         delay.setDelay (0.0f);
    118         for (int n = 0; n < buffer.getNumSamples(); ++n)
    119         {
    120             wowProcessor.updatePhase (ch);
    121             flutterProcessor.updatePhase (ch);
    122 
    123             delay.pushSample (ch, 0.0f);
    124             delay.popSample (ch);
    125         }
    126 
    127         wowProcessor.boundPhase (ch);
    128         flutterProcessor.boundPhase (ch);
    129     }
    130 }