AnalogTapeModel

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

commit 59a2d201641f483153fa03041a28e7d2569bc22a
parent 07f07589865992ced56c7322658fbe5f7a20a510
Author: jatinchowdhury18 <jatinchowdhury18@gmail.com>
Date:   Sun, 23 Aug 2020 17:54:50 -0700

Adjust dry/wet signal flow (#73)

* Adjust dry/wet signal flow

* Update Mac builds

* Add simple auto-update checking (#74)

* Add simple auto-update checking

* Update changelog

Co-authored-by: jatinchowdhury18 <jatinchowdhury18@users.noreply.github.com>

* Update latency compensation for dry/wet processing

* Update Mac builds

* Final tweaks to latency compensation

Co-authored-by: jatinchowdhury18 <jatinchowdhury18@users.noreply.github.com>
Co-authored-by: Travis CI <travis@Traviss-Mac-6.local>
Diffstat:
MCHANGELOG.md | 1+
MPlugin/Source/PluginProcessor.cpp | 48+++++++++++++++++++++++-------------------------
MPlugin/Source/PluginProcessor.h | 10+++++-----
MPlugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp | 4++--
4 files changed, 31 insertions(+), 32 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -6,6 +6,7 @@ this file. - Updated delay lines in wow/flutter processing to use 3-point Lagrange interpolation. - Change Newton-Raphson solver to use 4 or 8 iterations, in unrolled loop. +- Adjust signal flow so that input/output gains apply only to the wet signal. - Added simple behavior to automatically check for updates. - Improved loss filter sliders. - Fixed tooltip name sometimes not appearing. diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp @@ -144,14 +144,12 @@ void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesP hysteresis.prepareToPlay (sampleRate, samplesPerBlock); degrade.prepareToPlay (sampleRate, samplesPerBlock); chewer.prepare (sampleRate); + + dryDelay.prepare ({ sampleRate, (uint32) samplesPerBlock, 2 }); + dryDelay.setDelay (calcLatencySamples()); for (int ch = 0; ch < 2; ++ch) - { - dryDelay[ch].prepareToPlay (sampleRate, samplesPerBlock); - dryDelay[ch].setLengthMs (1000.0f * calcLatencySamples() / (float) sampleRate, true); - lossFilter[ch]->prepare ((float) sampleRate, samplesPerBlock); - } flutter.prepareToPlay (sampleRate, samplesPerBlock); outGain.prepareToPlay (sampleRate, samplesPerBlock); @@ -207,9 +205,8 @@ void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi outGain.setGain (Decibels::decibelsToGain (vts.getRawParameterValue ("outgain")->load())); dryWet.setDryWet (*vts.getRawParameterValue ("drywet") / 100.0f); - inGain.processBlock (buffer, midiMessages); - dryBuffer.makeCopyOf (buffer, true); + inGain.processBlock (buffer, midiMessages); hysteresis.processBlock (buffer, midiMessages); chewer.processBlock (buffer); @@ -220,30 +217,31 @@ void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi for (int ch = 0; ch < buffer.getNumChannels(); ++ch) lossFilter[ch]->processBlock (buffer.getWritePointer (ch), buffer.getNumSamples()); - // delay dry buffer to avoid phase issues - const auto latencySamp = roundToInt (calcLatencySamples()); - setLatencySamples (latencySamp); - for (int ch = 0; ch < buffer.getNumChannels(); ++ch) - { - auto* dryPtr = dryBuffer.getWritePointer (ch); - - // For "true bypass" use integer sample delay to avoid delay - // line interpolation freq. response issues - if (dryWet.getDryWet() < 0.2f) - dryDelay[ch].setLengthMs (1000.0f * latencySamp / (float) getSampleRate()); - else - dryDelay[ch].setLengthMs (1000.0f * calcLatencySamples() / (float) getSampleRate()); - - for (int n = 0; n < dryBuffer.getNumSamples(); ++n) - dryPtr[n] = dryDelay[ch].delay (dryPtr[n]); - } + latencyCompensation(); - dryWet.processBlock (dryBuffer, buffer); outGain.processBlock (buffer, midiMessages); + dryWet.processBlock (dryBuffer, buffer); scope->pushSamples (buffer); } +void ChowtapeModelAudioProcessor::latencyCompensation() +{ + // delay dry buffer to avoid phase issues + const auto latencySamp = roundToInt (calcLatencySamples()); + setLatencySamples (latencySamp); + + // For "true bypass" use integer sample delay to avoid delay + // line interpolation freq. response issues + if (dryWet.getDryWet() < 0.15f) + dryDelay.setDelay ((float) latencySamp); + else + dryDelay.setDelay (calcLatencySamples()); + + dsp::AudioBlock<float> block { dryBuffer }; + dryDelay.process (dsp::ProcessContextReplacing<float> { block }); +} + //============================================================================== bool ChowtapeModelAudioProcessor::hasEditor() const { diff --git a/Plugin/Source/PluginProcessor.h b/Plugin/Source/PluginProcessor.h @@ -67,12 +67,12 @@ public: void getStateInformation (MemoryBlock& destData) override; void setStateInformation (const void* data, int sizeInBytes) override; - AudioProcessorValueTreeState::ParameterLayout createParameterLayout(); - AudioProcessorValueTreeState& getVTS() { return vts; } - PresetManager& getPresetManager() { return presetManager; } - + private: + AudioProcessorValueTreeState::ParameterLayout createParameterLayout(); + void latencyCompensation(); + AudioProcessorValueTreeState vts; GainProcessor inGain; @@ -82,7 +82,7 @@ private: std::unique_ptr<LossFilter> lossFilter[2]; Flutter flutter; DryWetProcessor dryWet; - DelayProcessor dryDelay[2]; + dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Lagrange3rd> dryDelay { 1 << 21 }; GainProcessor outGain; AudioBuffer<float> dryBuffer; diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp b/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp @@ -151,8 +151,8 @@ void HysteresisProcessor::releaseResources() float HysteresisProcessor::getLatencySamples() const noexcept { - // latency of oversampling + fudge factor for Runge-Kutta and hysteresis - return overSample[curOS]->getLatencyInSamples() + 1.65f; + // latency of oversampling + fudge factor for hysteresis + return overSample[curOS]->getLatencyInSamples() + 1.4f; } void HysteresisProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& /*midi*/)