AnalogTapeModel

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

commit 62e3c81abd96222332497ae7fed401aaab369d1d
parent 654c561b431813d1c88adb83d4d97dd2cc5c7669
Author: jatinchowdhury18 <[email protected]>
Date:   Fri,  1 May 2020 12:31:36 -0700

Chew updates (#27)

* Updates to chew algorithm

* More Chew updates

* New chew builds

Co-authored-by: jatinchowdhury18 <[email protected]>
Diffstat:
MPlugin/Source/PluginProcessor.cpp | 6+++---
MPlugin/Source/Processors/Chew/ChewProcessor.cpp | 73++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
MPlugin/Source/Processors/Chew/ChewProcessor.h | 17+++++++++++++----
MPlugin/Source/Processors/Chew/Dropout.h | 68+++++++++++++++++++++++++++++++-------------------------------------
MPlugin/Source/Processors/Degrade/DegradeFilter.h | 8++++++--
MPlugin/Source/Processors/DryWetProcessor.h | 2+-
6 files changed, 120 insertions(+), 54 deletions(-)

diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp @@ -123,7 +123,7 @@ void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesP inGain.prepareToPlay (sampleRate, samplesPerBlock); hysteresis.prepareToPlay (sampleRate, samplesPerBlock); degrade.prepareToPlay (sampleRate, samplesPerBlock); - chewer.prepare (sampleRate, samplesPerBlock); + chewer.prepare (sampleRate); for (int ch = 0; ch < 2; ++ch) lossFilter[ch]->prepare ((float) sampleRate, samplesPerBlock); @@ -173,10 +173,10 @@ void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi inGain.setGain (Decibels::decibelsToGain (*vts.getRawParameterValue ("ingain"))); outGain.setGain (Decibels::decibelsToGain (*vts.getRawParameterValue ("outgain"))); - dryWet.setDryWet (*vts.getRawParameterValue ("drywet")); + dryWet.setDryWet (*vts.getRawParameterValue ("drywet") / 100.0f); inGain.processBlock (buffer, midiMessages); - + dryBuffer.makeCopyOf (buffer, true); hysteresis.processBlock (buffer, midiMessages); diff --git a/Plugin/Source/Processors/Chew/ChewProcessor.cpp b/Plugin/Source/Processors/Chew/ChewProcessor.cpp @@ -12,11 +12,13 @@ void ChewProcessor::createParameterLayout (std::vector<std::unique_ptr<RangedAud params.push_back (std::make_unique<AudioParameterFloat> ("chew_freq", "Freq", 0.0f, 1.0f, 0.0f)); } -void ChewProcessor::prepare (double sr, int samplesPerBlock) +void ChewProcessor::prepare (double sr) { sampleRate = (float) sr; - dropout.prepare (samplesPerBlock); + dropout.prepare (sr); + filt[0].reset (sampleRate, int (sr * 0.02)); + filt[1].reset (sampleRate, int (sr * 0.02)); isCrinkled = false; samplesUntilChange = getDryTime(); @@ -25,29 +27,86 @@ void ChewProcessor::prepare (double sr, int samplesPerBlock) void ChewProcessor::processBlock (AudioBuffer<float>& buffer) { - if (sampleCounter >= samplesUntilChange) + const int shortBlockSize = 64; + if (buffer.getNumSamples() <= shortBlockSize) + { + processShortBlock (buffer); + return; + } + + int sampleIdx = 0; + for(; sampleIdx + shortBlockSize <= buffer.getNumSamples(); sampleIdx += shortBlockSize) + { + AudioBuffer<float> shortBuff (buffer.getArrayOfWritePointers(), + buffer.getNumChannels(), sampleIdx, shortBlockSize); + + processShortBlock (shortBuff); + } + + if (sampleIdx < buffer.getNumSamples()) + { + AudioBuffer<float> shortBuff (buffer.getArrayOfWritePointers(), + buffer.getNumChannels(), sampleIdx, buffer.getNumSamples() - sampleIdx); + + processShortBlock (shortBuff); + } +} + +void ChewProcessor::processShortBlock (AudioBuffer<float>& buffer) +{ + const float highFreq = 22000.0f; + const float freqChange = highFreq - 5000.0f; + + if (*freq == 0.0f) + { + mix = 0.0f; + filt[0].setFreq (highFreq); + filt[1].setFreq (highFreq); + } + else if (*freq == 1.0f) + { + mix = 1.0f; + power = 3.0f * *depth; + filt[0].setFreq (highFreq - freqChange * *depth); + filt[1].setFreq (highFreq - freqChange * *depth); + } + else if (sampleCounter >= samplesUntilChange) { sampleCounter = 0; isCrinkled = ! isCrinkled; - + if (isCrinkled) // start crinkle { mix = 1.0f; - power = *depth; + power = (1.0f + 2.0f * random.nextFloat()) * *depth; + filt[0].setFreq (highFreq - freqChange * *depth); + filt[1].setFreq (highFreq - freqChange * *depth); samplesUntilChange = getWetTime(); } else // end crinkle { mix = 0.0f; - power = 0.0f; + filt[0].setFreq (highFreq); + filt[1].setFreq (highFreq); samplesUntilChange = getDryTime(); } } + else + { + power = (1.0f + 2.0f * random.nextFloat()) * *depth; + if (isCrinkled) + { + filt[0].setFreq (highFreq - freqChange * *depth); + filt[1].setFreq (highFreq - freqChange * *depth); + } + } dropout.setMix (mix); - dropout.setPow (1.0f + 4.0f * power); + dropout.setPower (1.0f + power); dropout.process (buffer); + for (int ch = 0; ch < buffer.getNumChannels(); ++ch) + filt[ch].process (buffer.getWritePointer (ch), buffer.getNumSamples()); sampleCounter += buffer.getNumSamples(); } diff --git a/Plugin/Source/Processors/Chew/ChewProcessor.h b/Plugin/Source/Processors/Chew/ChewProcessor.h @@ -2,6 +2,7 @@ #define CHEWPROCESSOR_H_INCLUDED #include "Dropout.h" +#include "../Degrade/DegradeFilter.h" class ChewProcessor { @@ -10,17 +11,18 @@ public: static void createParameterLayout (std::vector<std::unique_ptr<RangedAudioParameter>>& params); - void prepare (double sr, int samplesPerBlock); + void prepare (double sr); void processBlock (AudioBuffer<float>& buffer); + void processShortBlock (AudioBuffer<float>& buffer); private: float* depth = nullptr; float* freq = nullptr; - float mix = 0.0f; float power = 0.0f; Dropout dropout; + DegradeFilter filt[2]; Random random; int samplesUntilChange = 1000; @@ -31,12 +33,19 @@ private: inline int getDryTime() { - return random.nextInt (Range<int> ((int) ((1.0 - *freq) * sampleRate), (int) ((2 - 1.8 * *freq) * sampleRate))); + auto tScale = pow (*freq, 0.1f); + return random.nextInt (Range<int> ((int) ((1.0 - tScale) * sampleRate), + (int) ((2 - 1.99 * tScale) * sampleRate))); } inline int getWetTime() { - return random.nextInt (Range<int> ((int) ((0.05 + 0.05f * *depth) * sampleRate), (int) ((0.8 + 0.8 * *depth) * sampleRate))); + auto tScale = pow (*freq, 0.1f); + auto start = 0.2 + 0.8 * *depth; + auto end = start - (0.001 + 0.01 * *depth); + + return random.nextInt (Range<int> ((int) ((1.0 - tScale) * sampleRate), + (int) (((1.0 - tScale) + start - end * tScale) * sampleRate))); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChewProcessor) diff --git a/Plugin/Source/Processors/Chew/Dropout.h b/Plugin/Source/Processors/Chew/Dropout.h @@ -8,66 +8,60 @@ class Dropout public: Dropout() {} - void setMix (float newMix) { curMix = newMix; } - void setPow (float newPow) { power = newPow; } + void setMix (float newMix) + { + for (int ch = 0; ch < 2; ++ch) + mixSmooth[ch].setTargetValue (newMix); + } + + void setPower (float newPow) + { + for (int ch = 0; ch < 2; ++ch) + powerSmooth[ch].setTargetValue (newPow); + } - void prepare (int maxBufferSize) + void prepare (double sr) { - dryBuffer.setSize (2, maxBufferSize); + for (int ch = 0; ch < 2; ++ch) + { + mixSmooth[ch].reset (sr, 0.02); + mixSmooth[ch].setCurrentAndTargetValue (mixSmooth[ch].getTargetValue()); - prevMix = curMix; + powerSmooth[ch].reset (sr, 0.01); + powerSmooth[ch].setCurrentAndTargetValue (powerSmooth[ch].getTargetValue()); + } } void process (AudioBuffer<float>& buffer) { - if (curMix == 0.0f) + if (mixSmooth[0].getTargetValue() == 0.0f && ! mixSmooth[0].isSmoothing()) return; - // copy dry buffer for (int ch = 0; ch < buffer.getNumChannels(); ++ch) { - dryBuffer.copyFrom (ch, 0, buffer.getReadPointer (ch), buffer.getNumSamples()); - auto* x = buffer.getWritePointer (ch); for (int n = 0; n < buffer.getNumSamples(); ++n) - x[n] = dropout (x[n]); - } - - if (curMix == prevMix) - { - buffer.applyGain (curMix); - - for (int ch = 0; ch < buffer.getNumChannels(); ++ch) - buffer.addFrom (ch, 0, dryBuffer.getReadPointer (ch), buffer.getNumSamples(), (1.0f - curMix)); - } - else - { - buffer.applyGainRamp (0, buffer.getNumSamples(), prevMix, curMix); - - for (int ch = 0; ch < buffer.getNumChannels(); ++ch) - buffer.addFromWithRamp (ch, 0, dryBuffer.getReadPointer (ch), buffer.getNumSamples(), (1.0f - prevMix), (1.0f - curMix)); - - prevMix = curMix; + { + auto mix = mixSmooth[ch].getNextValue(); + x[n] = mix * dropout (x[n], ch) + (1.0f - mix) * x[n]; + } } } - inline float dropout (float x) + inline float dropout (float x, int ch) { float sign = 0.0f; - if (x < 0.0f) - sign = -1.0f; - else if (x > 0.0f) + if (x > 0.0f) sign = 1.0f; + else if (x < 0.0f) + sign = -1.0f; - return powf (abs (x), power) * sign / power; + return pow (abs (x), powerSmooth[ch].getNextValue()) * sign; } private: - float curMix = 0.0f; - float prevMix = curMix; - float power = 1.0f; - - AudioBuffer<float> dryBuffer; + LinearSmoothedValue<float> mixSmooth[2]; + LinearSmoothedValue<float> powerSmooth[2]; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Dropout) }; diff --git a/Plugin/Source/Processors/Degrade/DegradeFilter.h b/Plugin/Source/Processors/Degrade/DegradeFilter.h @@ -10,13 +10,17 @@ public: DegradeFilter() { freq.reset (numSteps); } ~DegradeFilter() {} - void reset (float sampleRate) + void reset (float sampleRate, int steps=0) { fs = sampleRate; for (int n = 0; n < 2; ++n) z[n] = 0.0f; - calcCoefs (freq.skip (numSteps)); + if (steps > 0) + freq.reset (steps); + + freq.setCurrentAndTargetValue (freq.getTargetValue()); + calcCoefs (freq.getCurrentValue()); } inline void calcCoefs (float fc) diff --git a/Plugin/Source/Processors/DryWetProcessor.h b/Plugin/Source/Processors/DryWetProcessor.h @@ -32,7 +32,7 @@ public: wetBuffer.addFromWithRamp (ch, 0, dryBuffer.getReadPointer (ch), wetBuffer.getNumSamples(), (1.0f - lastDryWet), (1.0f - dryWet)); } - + lastDryWet = dryWet; } }