commit 9d8c97a902ba5eeac9be18d423e2a2fd8d0e37ea
parent 292e0005300149e714265e743bfe0cfc745b3a01
Author: jatinchowdhury18 <jatinchowdhury18@gmail.com>
Date: Sun, 14 Jun 2020 17:24:56 -0700
Fix phase cancellation issues, latency reporting (#43)
* Fix phase cancellation issues, latency reporting
* Update pluginval
* Adjust plugivan commands
Co-authored-by: jatinchowdhury18 <jatinchowdhury18@users.noreply.github.com>
Diffstat:
7 files changed, 79 insertions(+), 49 deletions(-)
diff --git a/.travis.yml b/.travis.yml
@@ -99,24 +99,23 @@ script:
if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
echo "Validating plugin"
cd $TRAVIS_BUILD_DIR/
- curl -L "https://github.com/Tracktion/pluginval/releases/download/v0.2.3/pluginval_macOS.zip" -o pluginval.zip
+ curl -L "https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_macOS.zip" -o pluginval.zip
unzip pluginval
- pluginval.app/Contents/MacOS/pluginval --strictness-level 5 --validate-in-process --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/MacOSX/build/Release/CHOWTapeModel.vst3" || exit 1
- pluginval.app/Contents/MacOS/pluginval --strictness-level 5 --validate-in-process --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/MacOSX/build/Release/CHOWTapeModel.component" || exit 1
+ pluginval.app/Contents/MacOS/pluginval --strictness-level 8 --validate-in-process --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/MacOSX/build/Release/CHOWTapeModel.vst3" || exit 1
+ # pluginval.app/Contents/MacOS/pluginval --strictness-level 8 --validate-in-process --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/MacOSX/build/Release/CHOWTapeModel.component" || exit 1
fi
# - |
# if [[ $TRAVIS_OS_NAME == 'windows' ]]; then
# echo "Validating plugin"
# cd $TRAVIS_BUILD_DIR/
- # powershell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_Windows.zip -OutFile pluginval.zip"
- # powershell -Command "Expand-Archive pluginval.zip -DestinationPath ."
- # ./pluginval.exe --strictness-level 8 --validate-in-process --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/VisualStudio2017/x64/Debug/VST3/CHOWTapeModel.vst3"
+ # choco install pluginval
+ # pluginval.exe --strictness-level 8 --validate-in-process --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/VisualStudio2017/x64/Debug/VST3/CHOWTapeModel.vst3"
# fi
- |
if [[ $TRAVIS_OS_NAME == 'linux' ]]; then
curl -L "https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_Linux.zip" -o pluginval.zip
unzip pluginval
- ./pluginval --strictness-level 5 --validate-in-process --timeout-ms 500000 --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/LinuxMakefile/build/CHOWTapeModel.so" || exit 1
+ ./pluginval --strictness-level 8 --validate-in-process --timeout-ms 500000 --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/LinuxMakefile/build/CHOWTapeModel.so" || exit 1
fi
- echo "SUCCESS"
diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp
@@ -131,13 +131,18 @@ void ChowtapeModelAudioProcessor::changeProgramName (int index, const String& ne
//==============================================================================
void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
+ setRateAndBufferSizeDetails (sampleRate, samplesPerBlock);
+
inGain.prepareToPlay (sampleRate, samplesPerBlock);
hysteresis.prepareToPlay (sampleRate, samplesPerBlock);
degrade.prepareToPlay (sampleRate, samplesPerBlock);
chewer.prepare (sampleRate);
for (int ch = 0; ch < 2; ++ch)
+ {
+ dryDelay[ch].prepareToPlay (sampleRate, samplesPerBlock);
lossFilter[ch]->prepare ((float) sampleRate, samplesPerBlock);
+ }
flutter.prepareToPlay (sampleRate, samplesPerBlock);
outGain.prepareToPlay (sampleRate, samplesPerBlock);
@@ -147,6 +152,8 @@ void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesP
dryWet.setDryWet (*vts.getRawParameterValue ("drywet"));
dryWet.reset();
dryBuffer.setSize (2, samplesPerBlock);
+
+ setLatencySamples (roundToInt (calcLatencySamples()));
}
void ChowtapeModelAudioProcessor::releaseResources()
@@ -154,6 +161,11 @@ void ChowtapeModelAudioProcessor::releaseResources()
hysteresis.releaseResources();
}
+float ChowtapeModelAudioProcessor::calcLatencySamples() const noexcept
+{
+ return hysteresis.getLatencySamples();
+}
+
#ifndef JucePlugin_PreferredChannelConfigurations
bool ChowtapeModelAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
@@ -199,6 +211,17 @@ 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
+ setLatencySamples (roundToInt (calcLatencySamples()));
+ for (int ch = 0; ch < buffer.getNumChannels(); ++ch)
+ {
+ auto* dryPtr = dryBuffer.getWritePointer (ch);
+ dryDelay[ch].setLengthMs (1000.0f * calcLatencySamples() / (float) getSampleRate());
+
+ for (int n = 0; n < dryBuffer.getNumSamples(); ++n)
+ dryPtr[n] = dryDelay[ch].delay (dryPtr[n]);
+ }
+
dryWet.processBlock (dryBuffer, buffer);
outGain.processBlock (buffer, midiMessages);
diff --git a/Plugin/Source/PluginProcessor.h b/Plugin/Source/PluginProcessor.h
@@ -18,6 +18,7 @@
#include "Processors/Degrade/DegradeProcessor.h"
#include "Processors/Chew/ChewProcessor.h"
#include "Processors/DryWetProcessor.h"
+#include "Processors/Timing_Effects/DelayProcessor.h"
#include "Presets/PresetManager.h"
//==============================================================================
@@ -51,6 +52,7 @@ public:
bool producesMidi() const override;
bool isMidiEffect() const override;
double getTailLengthSeconds() const override;
+ float calcLatencySamples() const noexcept;
//==============================================================================
int getNumPrograms() override;
@@ -76,6 +78,7 @@ private:
std::unique_ptr<LossFilter> lossFilter[2];
Flutter flutter;
DryWetProcessor dryWet;
+ DelayProcessor dryDelay[2];
GainProcessor outGain;
AudioBuffer<float> dryBuffer;
diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp b/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp
@@ -117,6 +117,12 @@ void HysteresisProcessor::releaseResources()
overSample[i]->reset();
}
+float HysteresisProcessor::getLatencySamples() const noexcept
+{
+ // latency of oversampling + 1/2 sample latency from Runge-Kutta
+ return overSample[(int) *osParam]->getLatencyInSamples() + 0.5f;
+}
+
void HysteresisProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& /*midi*/)
{
setDrive (*driveParam);
diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.h b/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.h
@@ -23,6 +23,8 @@ public:
static void createParameterLayout (std::vector<std::unique_ptr<RangedAudioParameter>>& params);
+ float getLatencySamples() const noexcept;
+
void setDrive (float newDrive);
void setSaturation (float newSat);
void setWidth (float newWidth);
diff --git a/Plugin/Source/Processors/Timing_Effects/DelayProcessor.cpp b/Plugin/Source/Processors/Timing_Effects/DelayProcessor.cpp
@@ -1,12 +1,5 @@
#include "DelayProcessor.h"
-void DelayProcessor::setReadPtr (int maxLength)
-{
- readPtr++;
- if (readPtr >= maxLength) //wrap
- readPtr = 0;
-}
-
void DelayProcessor::setLengthMs (double lengthMs, bool force)
{
auto newLength = lengthMs * sampleRate / 1000.0;
@@ -49,35 +42,3 @@ void DelayProcessor::process (float* buffer, int numSamples)
for (int n = 0; n < numSamples; n++)
buffer[n] += delay (buffer[n]);
}
-
-float DelayProcessor::delay (float x)
-{
- // Erase head
- const int erasePtr = negativeAwareModulo (readPtr - 1, bufferSize);
- delayBuffer.setSample (0, erasePtr, 0.0f);
-
- // Write head
- const float len = length.getNextValue();
-
- float y = x;
- if (len > 0)
- {
- // Read head
- y = delayBuffer.getSample (0, readPtr);
-
- // write ptr
- const float fractionSample = len - (int) len;
- const int writePtr = (readPtr + (int) floorf (len)) % bufferSize;
-
- // feedback
- float writeSample = x + y * feedback;
-
- delayBuffer.addSample (0, writePtr, writeSample * (1.0f - fractionSample));
- delayBuffer.addSample (0, (writePtr + 1) % bufferSize, writeSample * fractionSample);
- }
-
- //update pointers
- setReadPtr (bufferSize);
-
- return y;
-}
diff --git a/Plugin/Source/Processors/Timing_Effects/DelayProcessor.h b/Plugin/Source/Processors/Timing_Effects/DelayProcessor.h
@@ -11,7 +11,38 @@ public:
void prepareToPlay (double sampleRate, int maxExpectedBlockSize);
void process (float* buffer, int numSamples);
- float delay (float x);
+
+ inline float delay (float x)
+ {
+ // Erase head
+ const int erasePtr = negativeAwareModulo (readPtr - 1, bufferSize);
+ delayBuffer.setSample (0, erasePtr, 0.0f);
+
+ // Write head
+ const float len = length.getNextValue();
+
+ float y = x;
+ if (len > 0)
+ {
+ // Read head
+ y = delayBuffer.getSample (0, readPtr);
+
+ // write ptr
+ const float fractionSample = len - (int) len;
+ const int writePtr = (readPtr + (int) floorf (len)) % bufferSize;
+
+ // feedback
+ float writeSample = x + y * feedback;
+
+ delayBuffer.addSample (0, writePtr, writeSample * (1.0f - fractionSample));
+ delayBuffer.addSample (0, (writePtr + 1) % bufferSize, writeSample * fractionSample);
+ }
+
+ //update pointers
+ setReadPtr (bufferSize);
+
+ return y;
+ }
void setLengthMs (double lengthMs, bool force = false);
float getLengthMS() const;
@@ -29,8 +60,13 @@ private:
float feedback = 0.0f;
int readPtr = 0;
- void resetPtrs() { readPtr = 0; }
- void setReadPtr (int maxLength);
+ inline void resetPtrs() { readPtr = 0; }
+ inline void setReadPtr (int maxLength)
+ {
+ readPtr++;
+ if (readPtr >= maxLength) //wrap
+ readPtr = 0;
+ }
float sampleRate = 48000.0f;