AnalogTapeModel

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

commit 252953fde9d6eef498b7f9728477eff13055b089
parent de8016b3b97a9e876aadb03ac04e342f332fd431
Author: jatinchowdhury18 <jatinchowdhury18@users.noreply.github.com>
Date:   Sat, 18 Apr 2020 19:45:00 -0700

Add tape degradation processing

Diffstat:
MPlugin/CHOWTapeModel.jucer | 8++++++++
MPlugin/Source/PluginProcessor.cpp | 4++++
MPlugin/Source/PluginProcessor.h | 2++
APlugin/Source/Processors/Degrade/DegradeFilter.h | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APlugin/Source/Processors/Degrade/DegradeNoise.h | 45+++++++++++++++++++++++++++++++++++++++++++++
APlugin/Source/Processors/Degrade/DegradeProcessor.cpp | 47+++++++++++++++++++++++++++++++++++++++++++++++
APlugin/Source/Processors/Degrade/DegradeProcessor.h | 32++++++++++++++++++++++++++++++++
MPlugin/Source/gui.xml | 0
8 files changed, 206 insertions(+), 0 deletions(-)

diff --git a/Plugin/CHOWTapeModel.jucer b/Plugin/CHOWTapeModel.jucer @@ -8,6 +8,14 @@ <GROUP id="{0178B10A-4A61-796A-5AB2-915D32AF6EEE}" name="Source"> <FILE id="Xn3V5v" name="gui.xml" compile="0" resource="1" file="Source/gui.xml"/> <GROUP id="{43BBFC88-4D0A-01B8-2635-3748470B94F4}" name="Processors"> + <GROUP id="{344B63D7-2DBC-F9D2-ACD7-1B0671D4D024}" name="Degrade"> + <FILE id="ac7jRp" name="DegradeFilter.h" compile="0" resource="0" file="Source/Processors/Degrade/DegradeFilter.h"/> + <FILE id="qWC1GC" name="DegradeNoise.h" compile="0" resource="0" file="Source/Processors/Degrade/DegradeNoise.h"/> + <FILE id="VIsORm" name="DegradeProcessor.cpp" compile="1" resource="0" + file="Source/Processors/Degrade/DegradeProcessor.cpp"/> + <FILE id="pXjWJR" name="DegradeProcessor.h" compile="0" resource="0" + file="Source/Processors/Degrade/DegradeProcessor.h"/> + </GROUP> <GROUP id="{6052B1B0-83EF-DBFA-991C-FC0B47A949C9}" name="Hysteresis"> <FILE id="Qe4tlV" name="HysteresisProcessing.cpp" compile="1" resource="0" file="Source/Processors/Hysteresis/HysteresisProcessing.cpp"/> diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp @@ -24,6 +24,7 @@ ChowtapeModelAudioProcessor::ChowtapeModelAudioProcessor() #endif vts (*this, nullptr, Identifier ("Parameters"), createParameterLayout()), hysteresis (vts), + degrade (vts), flutter (vts) { for (int ch = 0; ch < 2; ++ch) @@ -46,6 +47,7 @@ AudioProcessorValueTreeState::ParameterLayout ChowtapeModelAudioProcessor::creat HysteresisProcessor::createParameterLayout (params); LossFilter::createParameterLayout (params); Flutter::createParameterLayout (params); + DegradeProcessor::createParameterLayout (params); return { params.begin(), params.end() }; } @@ -117,6 +119,7 @@ void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesP { inGain.prepareToPlay (sampleRate, samplesPerBlock); hysteresis.prepareToPlay (sampleRate, samplesPerBlock); + degrade.prepareToPlay (sampleRate, samplesPerBlock); for (int ch = 0; ch < 2; ++ch) lossFilter[ch]->prepare ((float) sampleRate, samplesPerBlock); @@ -165,6 +168,7 @@ void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi inGain.processBlock (buffer, midiMessages); hysteresis.processBlock (buffer, midiMessages); + degrade.processBlock (buffer, midiMessages); flutter.processBlock (buffer, midiMessages); diff --git a/Plugin/Source/PluginProcessor.h b/Plugin/Source/PluginProcessor.h @@ -15,6 +15,7 @@ #include "Processors/Hysteresis/HysteresisProcessor.h" #include "Processors/Loss_Effects/LossFilter.h" #include "Processors/Timing_Effects/Flutter.h" +#include "Processors/Degrade/DegradeProcessor.h" //============================================================================== /** @@ -67,6 +68,7 @@ private: GainProcessor inGain; HysteresisProcessor hysteresis; + DegradeProcessor degrade; std::unique_ptr<LossFilter> lossFilter[2]; Flutter flutter; GainProcessor outGain; diff --git a/Plugin/Source/Processors/Degrade/DegradeFilter.h b/Plugin/Source/Processors/Degrade/DegradeFilter.h @@ -0,0 +1,68 @@ +#ifndef DEGRADEFILTER_H_INCLUDED +#define DEGRADEFILTER_H_INCLUDED + +#include "JuceHeader.h" + +/** Lowpass filter for tape degrade effect */ +class DegradeFilter +{ +public: + DegradeFilter() { freq.reset (numSteps); } + ~DegradeFilter() {} + + void reset (float sampleRate) + { + fs = sampleRate; + for (int n = 0; n < 2; ++n) + z[n] = 0.0f; + + calcCoefs (freq.skip (numSteps)); + } + + inline void calcCoefs (float fc) + { + float wc = MathConstants<float>::twoPi * fc / fs; + float c = 1.0f / dsp::FastMathApproximations::tan (wc / 2.0f); + float a0 = c + 1.0f; + + b[0] = 1 / a0; + b[1] = b[0]; + a[1] = (1.0f - c) / a0; + } + + inline void process (float* buffer, int numSamples) + { + for (int n = 0; n < numSamples; ++n) + { + if (freq.isSmoothing()) + calcCoefs (freq.getNextValue()); + + buffer[n] = processSample (buffer[n]); + } + } + + inline float processSample (float x) + { + float y = z[1] + x*b[0]; + z[1] = x*b[1] - y*a[1]; + return y; + } + + void setFreq (float newFreq) + { + freq.setTargetValue (newFreq); + } + +private: + SmoothedValue<float, ValueSmoothingTypes::Multiplicative> freq = 20000.0f; + float fs = 44100.0f; + const int numSteps = 200; + + float a[2] = { 1.0f, 0.0f }; + float b[2] = { 1.0f, 0.0f }; + float z[2] = { 1.0f, 0.0f }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DegradeFilter) +}; + +#endif // DEGRADEFILTER_H_INCLUDED diff --git a/Plugin/Source/Processors/Degrade/DegradeNoise.h b/Plugin/Source/Processors/Degrade/DegradeNoise.h @@ -0,0 +1,45 @@ +#ifndef DEGRADENOISE_H_INCLUDED +#define DEGRADENOISE_H_INCLUDED + +#include "JuceHeader.h" + +/** Noise for tape degrade effect */ +class DegradeNoise +{ +public: + DegradeNoise() {} + ~DegradeNoise() {} + + void setGain (float newGain) { curGain = newGain; } + + void prepare() + { + prevGain = curGain; + } + + void processBlock (float* buffer, int numSamples) + { + if (curGain == prevGain) + { + for (int n = 0; n < numSamples; ++n) + buffer[n] += (random.nextFloat() - 0.5f) * curGain; + } + else + { + for (int n = 0; n < numSamples; ++n) + buffer[n] += (random.nextFloat() - 0.5f) * ((curGain * (float) n / (float) numSamples) + (prevGain * (1.0f - (float) n / (float) numSamples))); + + prevGain = curGain; + } + } + +private: + float curGain = 0.0f; + float prevGain = curGain; + + Random random; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DegradeNoise) +}; + +#endif // DEGRADENOISE_H_INCLUDED diff --git a/Plugin/Source/Processors/Degrade/DegradeProcessor.cpp b/Plugin/Source/Processors/Degrade/DegradeProcessor.cpp @@ -0,0 +1,47 @@ +#include "DegradeProcessor.h" + +DegradeProcessor::DegradeProcessor (AudioProcessorValueTreeState& vts) +{ + depthParam = vts.getRawParameterValue ("deg_depth"); + amtParam = vts.getRawParameterValue ("deg_amt"); + varParam = vts.getRawParameterValue ("deg_var"); +} + +void DegradeProcessor::createParameterLayout (std::vector<std::unique_ptr<RangedAudioParameter>>& params) +{ + params.push_back (std::make_unique<AudioParameterFloat> ("deg_depth", "Depth", 0.0f, 1.0f, 0.0f)); + params.push_back (std::make_unique<AudioParameterFloat> ("deg_amt", "Amount", 0.0f, 1.0f, 0.0f)); + params.push_back (std::make_unique<AudioParameterFloat> ("deg_var", "Variance", 0.0f, 1.0f, 0.0f)); +} + +void DegradeProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) +{ + for (int ch = 0; ch < 2; ++ch) + { + noiseProc[ch].prepare(); + filterProc[ch].reset ((float) sampleRate); + } + + gainProc.prepareToPlay (sampleRate, samplesPerBlock); +} + +void DegradeProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi) +{ + if (*amtParam == 0.0f || *depthParam == 0.0f) + return; + + float freqHz = 200.0f * std::powf (20000.0f / 200.0f, 1.0f - *amtParam); + float gainDB = -24.0f * *depthParam; + + for (int ch = 0; ch < buffer.getNumChannels(); ++ch) + { + noiseProc[ch].setGain (0.5f * *depthParam * *amtParam); + noiseProc[ch].processBlock (buffer.getWritePointer (ch), buffer.getNumSamples()); + + filterProc[ch].setFreq (jmin (freqHz + (*varParam * (freqHz / 0.6f) * (random.nextFloat() - 0.5f)), 22000.0f)); + filterProc[ch].process (buffer.getWritePointer (ch), buffer.getNumSamples()); + } + + gainProc.setGain (Decibels::decibelsToGain (jmin (gainDB + (*varParam * 36.0f * (random.nextFloat() - 0.5f)), 3.0f))); + gainProc.processBlock (buffer, midi); +} diff --git a/Plugin/Source/Processors/Degrade/DegradeProcessor.h b/Plugin/Source/Processors/Degrade/DegradeProcessor.h @@ -0,0 +1,32 @@ +#ifndef DEGRADEPROCESSOR_H_INCLUDED +#define DEGRADEPROCESSOR_H_INCLUDED + +#include "../GainProcessor.h" +#include "DegradeNoise.h" +#include "DegradeFilter.h" + +class DegradeProcessor +{ +public: + DegradeProcessor (AudioProcessorValueTreeState& vts); + + static void createParameterLayout (std::vector<std::unique_ptr<RangedAudioParameter>>& params); + + void prepareToPlay (double sampleRate, int samplesPerBlock); + void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi); + +private: + float* depthParam = nullptr; + float* amtParam = nullptr; + float* varParam = nullptr; + + DegradeNoise noiseProc[2]; + DegradeFilter filterProc[2]; + GainProcessor gainProc; + + Random random; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DegradeProcessor) +}; + +#endif // DEGRADEPROCESSOR_H_INCLUDED diff --git a/Plugin/Source/gui.xml b/Plugin/Source/gui.xml Binary files differ.