From bb28a48dde2d98cbfa95b9b20975c24c0a8aa46b Mon Sep 17 00:00:00 2001
From: Sophia Poirier <2997196+sophiapoirier@users.noreply.github.com>
Date: Sat, 1 Oct 2022 18:15:08 -0700
Subject: [PATCH 1/4] Buffer Override: fix lack of pixel quantization due to
implicit floating point to integer conversion (as well as a few more implicit
conversions)
---
bufferoverride/gui/bufferoverrideview.cpp | 12 ++++++------
dfx-library/dfx-au-utilities-preset-files.m | 6 +++---
dfx-library/dfxsettings.cpp | 6 +++---
dfxgui/dfxguislider.cpp | 2 +-
4 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/bufferoverride/gui/bufferoverrideview.cpp b/bufferoverride/gui/bufferoverrideview.cpp
index 8d97a8bf..d93693fe 100644
--- a/bufferoverride/gui/bufferoverrideview.cpp
+++ b/bufferoverride/gui/bufferoverrideview.cpp
@@ -16,7 +16,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Buffer Override. If not, see .
-To contact the author, use the contact form at http://destroyfx.org/
+To contact the author, use the contact form at http://destroyfx.org
------------------------------------------------------------------------*/
#include "bufferoverrideview.h"
@@ -177,20 +177,20 @@ void BufferOverrideView::draw(VSTGUI::CDrawContext *ctx) {
// Place boxes in float space but round to integer coordinates
// since we leave some thin pixel borders.
for (CCoord xpos = MARGIN_HORIZ; xpos < width; xpos += majorbox_width) {
- const int ixpos = std::lround(xpos);
- const int majw = std::lround(xpos + majorbox_width) - xpos;
+ const auto ixpos = std::lround(xpos);
+ const auto majw = std::lround(xpos + majorbox_width - xpos);
DrawBox(ixpos, MARGIN_TOP, majw, majorbox_height, color_lite);
auto minorbox_color = color_lite;
for (CCoord nxpos = 2.0; nxpos < majw - 2; nxpos += minorbox_width) {
- const int inxpos = std::lround(nxpos);
- const int minw = std::lround(nxpos + minorbox_width) - inxpos;
+ const auto inxpos = std::lround(nxpos);
+ const auto minw = std::lround(nxpos + minorbox_width) - inxpos;
// Clip the width of the last minibuffer.
// We can reuse the right margin by overlapping it with the
// space after the last box, so just majw - 1.
- const int w = std::min(minw, (majw - 1) - inxpos);
+ const auto w = std::min(minw, (majw - 1) - inxpos);
DrawFilledBox(ixpos + inxpos, MARGIN_TOP + 2,
// leave space between boxes
diff --git a/dfx-library/dfx-au-utilities-preset-files.m b/dfx-library/dfx-au-utilities-preset-files.m
index a8fea09a..7ce906af 100644
--- a/dfx-library/dfx-au-utilities-preset-files.m
+++ b/dfx-library/dfx-au-utilities-preset-files.m
@@ -1,7 +1,7 @@
/*
Destroy FX AU Utilities is a collection of helpful utility functions
for creating and hosting Audio Unit plugins.
- Copyright (C) 2003-2021 Sophia Poirier
+ Copyright (C) 2003-2022 Sophia Poirier
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -942,12 +942,12 @@ OSStatus WritePropertyListToXMLFile(CFPropertyListRef inPropertyList, CFURLRef i
{
errorCode = CFErrorGetCode(errorRef);
CFRelease(errorRef);
- return errorCode;
+ return (OSStatus)errorCode;
}
return kDFX_coreFoundationUnknownErr;
}
- return errorCode;
+ return (OSStatus)errorCode;
}
//-----------------------------------------------------------------------------
diff --git a/dfx-library/dfxsettings.cpp b/dfx-library/dfxsettings.cpp
index e0fe7af6..c79881c3 100644
--- a/dfx-library/dfxsettings.cpp
+++ b/dfx-library/dfxsettings.cpp
@@ -808,11 +808,11 @@ bool DfxSettings::restoreMidiAssignmentsFromDictionary(CFDictionaryRef inDiction
if (assignmentCFDictionary)
{
auto const parameterID_optional = DFX_GetNumberFromCFDictionary_i(assignmentCFDictionary, kDfxSettings_ParameterIDKey);
- if (!parameterID_optional)
+ if (!parameterID_optional || (*parameterID_optional < 0))
{
continue;
}
- auto const parameterID = getParameterIndexFromMap(*parameterID_optional);
+ auto const parameterID = getParameterIndexFromMap(static_cast(*parameterID_optional));
if (!isValidParameterID(parameterID))
{
continue;
@@ -1394,7 +1394,7 @@ dfx::ParameterID DfxSettings::getParameterIndexFromMap(dfx::ParameterID inParame
auto const foundID = std::find(inSearchIDs.begin(), inSearchIDs.end(), inParameterID);
if (foundID != inSearchIDs.end())
{
- return std::distance(inSearchIDs.begin(), foundID);
+ return dfx::math::ToIndex(std::distance(inSearchIDs.begin(), foundID));
}
return dfx::kParameterID_Invalid;
}
diff --git a/dfxgui/dfxguislider.cpp b/dfxgui/dfxguislider.cpp
index 00cacaf5..dc8ba581 100644
--- a/dfxgui/dfxguislider.cpp
+++ b/dfxgui/dfxguislider.cpp
@@ -827,7 +827,7 @@ DGAnimation::DGAnimation(DfxGuiEditor* inOwnerEditor,
DGImage* inAnimationImage,
size_t inNumAnimationFrames)
: DGControl(inRegion, inOwnerEditor, dfx::ParameterID_ToVST(inParameterID),
- inNumAnimationFrames, inRegion.getHeight(), inAnimationImage)
+ static_cast(inNumAnimationFrames), inRegion.getHeight(), inAnimationImage)
{
assert(inNumAnimationFrames > 0);
From 2b8886253729fec87dd0defa3c4f80f8b63f02b8 Mon Sep 17 00:00:00 2001
From: Sophia Poirier <2997196+sophiapoirier@users.noreply.github.com>
Date: Sun, 2 Oct 2022 19:44:46 -0700
Subject: [PATCH 2/4] use size_t for array size arguments to
dfx::math::InterpolateHermite and IIRFilter::processToCacheH2/3/4
---
dfx-library/dfxmath.h | 29 ++++---
dfx-library/iirfilter.h | 15 +++-
scrubby/scrubbyprocess.cpp | 2 +-
transverb/transverbprocess.cpp | 10 +--
turntablist/turntablist.cpp | 83 +++----------------
turntablist/turntablist.h | 147 ++++++++++++++++-----------------
6 files changed, 121 insertions(+), 165 deletions(-)
diff --git a/dfx-library/dfxmath.h b/dfx-library/dfxmath.h
index 1d5862b1..a35680e1 100644
--- a/dfx-library/dfxmath.h
+++ b/dfx-library/dfxmath.h
@@ -18,7 +18,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Destroy FX Library. If not, see .
-To contact the author, use the contact form at http://destroyfx.org/
+To contact the author, use the contact form at http://destroyfx.org
Destroy FX is a sovereign entity comprised of Sophia Poirier and Tom Murphy 7.
This is our math and numerics shit.
@@ -153,15 +153,19 @@ constexpr T FrequencyScalarBySemitones(T inSemitones)
}
//-----------------------------------------------------------------------------
-constexpr float InterpolateHermite(float const* inData, double inAddress, long inBufferSize)
+constexpr float InterpolateHermite(float const* inData, double inAddress, size_t inBufferSize)
{
- auto const pos = static_cast(inAddress);
+ assert(inAddress >= 0.);
+ assert(inBufferSize > 0);
+
+ auto const pos = static_cast(inAddress);
+ assert(pos < inBufferSize);
auto const posFract = static_cast(inAddress - static_cast(pos));
#if 0 // XXX test performance using fewer variables/registers
- long const posMinus1 = (pos == 0) ? (inBufferSize - 1) : (pos - 1);
- long const posPlus1 = (pos + 1) % inBufferSize;
- long const posPlus2 = (pos + 2) % inBufferSize;
+ size_t const posMinus1 = (pos == 0) ? (inBufferSize - 1) : (pos - 1);
+ size_t const posPlus1 = (pos + 1) % inBufferSize;
+ size_t const posPlus2 = (pos + 2) % inBufferSize;
return (((((((3.0f * (inData[pos] - inData[posPlus1])) - inData[posMinus1] + inData[posPlus2]) * 0.5f) * posFract)
+ ((2.0f * inData[posPlus1]) + inData[posMinus1] - (2.5f * inData[pos]) - (inData[posPlus2] * 0.5f))) *
@@ -179,9 +183,9 @@ constexpr float InterpolateHermite(float const* inData, double inAddress, long i
return ((((a * posFract) + b) * posFract + c) * posFract) + inData[pos];
#else
- long const posMinus1 = (pos == 0) ? (inBufferSize - 1) : (pos - 1);
- long const posPlus1 = (pos + 1) % inBufferSize;
- long const posPlus2 = (pos + 2) % inBufferSize;
+ size_t const posMinus1 = (pos == 0) ? (inBufferSize - 1) : (pos - 1);
+ size_t const posPlus1 = (pos + 1) % inBufferSize;
+ size_t const posPlus2 = (pos + 2) % inBufferSize;
float const a = ((3.0f * (inData[pos] - inData[posPlus1])) - inData[posMinus1] + inData[posPlus2]) * 0.5f;
float const b = (2.0f * inData[posPlus1]) + inData[posMinus1] - (2.5f * inData[pos]) - (inData[posPlus2] * 0.5f);
@@ -192,9 +196,12 @@ constexpr float InterpolateHermite(float const* inData, double inAddress, long i
}
//-----------------------------------------------------------------------------
-constexpr float InterpolateHermite_NoWrap(float const* inData, double inAddress, long inBufferSize)
+constexpr float InterpolateHermite_NoWrap(float const* inData, double inAddress, size_t inBufferSize)
{
- auto const pos = static_cast(inAddress);
+ assert(inAddress >= 0.);
+
+ auto const pos = static_cast(inAddress);
+ assert(pos < inBufferSize);
auto const posFract = static_cast(inAddress - static_cast(pos));
float const dataPosMinus1 = (pos == 0) ? 0.0f : inData[pos - 1];
diff --git a/dfx-library/iirfilter.h b/dfx-library/iirfilter.h
index 51951ce3..054b2292 100644
--- a/dfx-library/iirfilter.h
+++ b/dfx-library/iirfilter.h
@@ -18,7 +18,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Destroy FX Library. If not, see .
-To contact the author, use the contact form at http://destroyfx.org/
+To contact the author, use the contact form at http://destroyfx.org
Destroy FX is a sovereign entity comprised of Sophia Poirier and Tom Murphy 7.
Welcome to our Infinite Impulse Response filter.
@@ -28,6 +28,7 @@ Welcome to our Infinite Impulse Response filter.
#include
+#include
#include
#include "dfxmath.h"
@@ -131,8 +132,10 @@ class IIRFilter
mPrevIn = inSample;
}
- void processToCacheH2(float * inAudio, long inPos, long inBufferSize)
+ void processToCacheH2(float const* inAudio, size_t inPos, size_t inBufferSize)
{
+ assert(inBufferSize > 0);
+ assert(inPos < inBufferSize);
auto const in0 = inAudio[inPos];
auto const in1 = inAudio[(inPos + 1) % inBufferSize];
@@ -154,8 +157,10 @@ class IIRFilter
mPrevIn = in1;
}
- void processToCacheH3(float * inAudio, long inPos, long inBufferSize)
+ void processToCacheH3(float const* inAudio, size_t inPos, size_t inBufferSize)
{
+ assert(inBufferSize > 0);
+ assert(inPos < inBufferSize);
auto const in0 = inAudio[inPos];
auto const in1 = inAudio[(inPos + 1) % inBufferSize];
auto const in2 = inAudio[(inPos + 2) % inBufferSize];
@@ -178,8 +183,10 @@ class IIRFilter
mPrevIn = in2;
}
- void processToCacheH4(float * inAudio, long inPos, long inBufferSize)
+ void processToCacheH4(float const* inAudio, size_t inPos, size_t inBufferSize)
{
+ assert(inBufferSize > 0);
+ assert(inPos < inBufferSize);
auto const in0 = inAudio[inPos];
auto const in1 = inAudio[(inPos + 1) % inBufferSize];
auto const in2 = inAudio[(inPos + 2) % inBufferSize];
diff --git a/scrubby/scrubbyprocess.cpp b/scrubby/scrubbyprocess.cpp
index cb298da2..d65a1c6c 100644
--- a/scrubby/scrubbyprocess.cpp
+++ b/scrubby/scrubbyprocess.cpp
@@ -446,7 +446,7 @@ void Scrubby::processaudio(float const* const* inAudio, float* const* outAudio,
for (size_t ch = 0; ch < numChannels; ch++)
{
auto const inputValue = (ch < getnuminputs()) ? inAudio[ch][sampleIndex] : inputValue_firstChannel;
- auto outputValue = dfx::math::InterpolateHermite(mAudioBuffers[ch].data(), mReadPos[ch], mMaxAudioBufferSize);
+ auto outputValue = dfx::math::InterpolateHermite(mAudioBuffers[ch].data(), mReadPos[ch], dfx::math::ToUnsigned(mMaxAudioBufferSize));
outputValue = mHighpassFilters[ch].process(outputValue);
outAudio[ch][sampleIndex] = (inputValue * mInputGain.getValue()) + (outputValue * mOutputGain.getValue());
}
diff --git a/transverb/transverbprocess.cpp b/transverb/transverbprocess.cpp
index 871c0aaa..7f2f06c8 100644
--- a/transverb/transverbprocess.cpp
+++ b/transverb/transverbprocess.cpp
@@ -16,7 +16,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Transverb. If not, see .
-To contact the author, use the contact form at http://destroyfx.org/
+To contact the author, use the contact form at http://destroyfx.org
------------------------------------------------------------------------*/
#include "transverb.h"
@@ -214,17 +214,17 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, size_t numSamp
lowpasscount++;
break;
case 2:
- heads[h].filter.processToCacheH2(heads[h].buf.data(), lowpasspos[h], bsize);
+ heads[h].filter.processToCacheH2(heads[h].buf.data(), dfx::math::ToUnsigned(lowpasspos[h]), dfx::math::ToUnsigned(bsize));
lowpasspos[h] = (lowpasspos[h] + 2) % bsize;
lowpasscount += 2;
break;
case 3:
- heads[h].filter.processToCacheH3(heads[h].buf.data(), lowpasspos[h], bsize);
+ heads[h].filter.processToCacheH3(heads[h].buf.data(), dfx::math::ToUnsigned(lowpasspos[h]), dfx::math::ToUnsigned(bsize));
lowpasspos[h] = (lowpasspos[h] + 3) % bsize;
lowpasscount += 3;
break;
default:
- heads[h].filter.processToCacheH4(heads[h].buf.data(), lowpasspos[h], bsize);
+ heads[h].filter.processToCacheH4(heads[h].buf.data(), dfx::math::ToUnsigned(lowpasspos[h]), dfx::math::ToUnsigned(bsize));
lowpasspos[h] = (lowpasspos[h] + 4) % bsize;
lowpasscount += 4;
break;
@@ -311,7 +311,7 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, size_t numSamp
delayvals[h] = interpolateLinear(buf.data(), heads[h].read, bsize);
break;
case kQualityMode_UltraHiFi:
- delayvals[h] = dfx::math::InterpolateHermite(buf.data(), heads[h].read, bsize);
+ delayvals[h] = dfx::math::InterpolateHermite(buf.data(), heads[h].read, dfx::math::ToUnsigned(bsize));
break;
}
}
diff --git a/turntablist/turntablist.cpp b/turntablist/turntablist.cpp
index 47b1b8ef..6485950c 100644
--- a/turntablist/turntablist.cpp
+++ b/turntablist/turntablist.cpp
@@ -123,48 +123,15 @@ Turntablist::Turntablist(TARGET_API_BASE_INSTANCE_TYPE inInstance)
setSupportedLogicNodeOperationMode(0);
#endif
- m_nNumChannels = 0;
- m_nNumSamples = 0;
- m_nSampleRate = 0;
- m_fSampleRate = 0.0;
-
-
- m_bPlayedReverse = false;
- m_bPlay = false;
-
- m_fPosition = 0.0;
- m_fPosOffset = 0.0;
- m_fNumSamples = 0.0;
m_fLastScratchAmount = getparameter_f(kParam_ScratchAmount);
m_fPitchBend = k_fScratchAmountMiddlePoint;
- m_bPitchBendSet = false;
- m_bScratching = false;
- m_bWasScratching = false;
- m_bPlayForward = true;
m_nScratchSubMode = 1; // speed based from scrub scratch mode
- m_nScratchDelay = 0;
-
m_nRootKey = static_cast(getparameter_i(kParam_RootKey));
m_nCurrentNote = m_nRootKey;
m_nCurrentVelocity = 0x7F;
-
- m_bAudioFileHasBeenLoaded = false;
- m_bScratchStop = false;
-
- memset(&m_fsAudioFile, 0, sizeof(m_fsAudioFile));
-
- m_fScratchVolumeModifier = 0.0f;
-
- m_bScratchAmountSet = false;
- m_fDesiredScratchRate = 0.0;
-
- m_nScratchInterval = 0;
-
- m_fDesiredPosition = 0.0;
- m_fPrevDesiredPosition = 0.0;
}
//-----------------------------------------------------------------------------------------
@@ -773,7 +740,7 @@ OSStatus Turntablist::loadAudioFile(FSRef const& inFileRef)
m_nNumChannels = clientStreamFormat.mChannelsPerFrame;
m_nSampleRate = clientStreamFormat.mSampleRate;
- m_nNumSamples = static_cast(audioFileNumFrames);
+ m_nNumSamples = dfx::math::ToUnsigned(audioFileNumFrames);
m_fPlaySampleRate = static_cast(m_nSampleRate);
m_fSampleRate = static_cast(m_nSampleRate);
@@ -820,9 +787,9 @@ OSStatus Turntablist::loadAudioFile(FSRef const& inFileRef)
std::unique_lock guard(m_AudioFileLock);
- m_nNumChannels = sfInfo.channels;
+ m_nNumChannels = dfx::math::ToUnsigned(sfInfo.channels);
m_nSampleRate = sfInfo.samplerate;
- m_nNumSamples = static_cast(sfInfo.frames);
+ m_nNumSamples = dfx::math::ToUnsigned(sfInfo.frames);
m_fPlaySampleRate = static_cast(m_nSampleRate);
m_fSampleRate = static_cast(m_nSampleRate);
@@ -953,17 +920,11 @@ void Turntablist::processaudio(float const* const* /*inAudio*/, float* const* ou
// speed mode
if (m_fTinyScratchAdjust > 0.0) // positive
{
- if (m_fPlaySampleRate > m_fDesiredScratchRate2)
- {
- m_fPlaySampleRate = m_fDesiredScratchRate2;
- }
+ m_fPlaySampleRate = std::min(m_fPlaySampleRate, m_fDesiredScratchRate2);
}
else // negative
{
- if (m_fPlaySampleRate < m_fDesiredScratchRate2)
- {
- m_fPlaySampleRate = m_fDesiredScratchRate2;
- }
+ m_fPlaySampleRate = std::max(m_fPlaySampleRate, m_fDesiredScratchRate2);
}
}
}
@@ -974,30 +935,18 @@ void Turntablist::processaudio(float const* const* /*inAudio*/, float* const* ou
{
if (m_fPlaySampleRate > 0.0) // too high, spin down
{
- m_fPlaySampleRate -= m_fUsedSpinDownSpeed; // adjust
- if (m_fPlaySampleRate < 0.0) // too low so we past it
- {
- m_fPlaySampleRate = 0.0; // fix it
- }
+ m_fPlaySampleRate = std::max(m_fPlaySampleRate - m_fUsedSpinDownSpeed, 0.);
}
}
else // power on - spin up
{
if (m_fPlaySampleRate < m_fDesiredPitch) // too low so bring up
{
- m_fPlaySampleRate += m_fUsedSpinUpSpeed; // adjust
- if (m_fPlaySampleRate > m_fDesiredPitch) // too high so we past it
- {
- m_fPlaySampleRate = m_fDesiredPitch; // fix it
- }
+ m_fPlaySampleRate = std::min(m_fPlaySampleRate + m_fUsedSpinUpSpeed, m_fDesiredPitch);
}
else if (m_fPlaySampleRate > m_fDesiredPitch) // too high so bring down
{
- m_fPlaySampleRate -= m_fUsedSpinUpSpeed; // adjust
- if (m_fPlaySampleRate < m_fDesiredPitch) // too low so we past it
- {
- m_fPlaySampleRate = m_fDesiredPitch; // fix it
- }
+ m_fPlaySampleRate = std::max(m_fPlaySampleRate - m_fUsedSpinUpSpeed, m_fDesiredPitch);
}
}
}
@@ -1106,8 +1055,8 @@ void Turntablist::processaudio(float const* const* /*inAudio*/, float* const* ou
#ifdef LINEAR_INTERPOLATION
float const floating_part = m_fPosition - static_cast(static_cast(m_fPosition));
- long const big_part1 = static_cast(m_fPosition);
- long big_part2 = big_part1 + 1;
+ auto const big_part1 = static_cast(m_fPosition);
+ auto big_part2 = big_part1 + 1;
if (big_part2 > m_nNumSamples)
{
big_part2 = 0;
@@ -1303,6 +1252,7 @@ void Turntablist::processScratch(bool inSetParameter)
if (m_nScratchInterval == 0)
{
m_fPosition = m_fDesiredPosition;
+ assert(m_fPosition >= 0.);
m_fTinyScratchAdjust = 0.0;
}
else
@@ -1401,16 +1351,9 @@ void Turntablist::processScratch(bool inSetParameter)
m_fLastScratchAmount = m_fScratchAmount;
}
- m_fScratchVolumeModifier = m_fPlaySampleRate / m_fSampleRate;
- if (m_fScratchVolumeModifier > 1.5f)
- {
- m_fScratchVolumeModifier = 1.5f;
- }
+ m_fScratchVolumeModifier = std::min(m_fPlaySampleRate / m_fSampleRate, 1.5);
- if (!m_bScratching)
- {
- m_bScratching = true;
- }
+ m_bScratching = true;
if (m_bScratchStop)
{
diff --git a/turntablist/turntablist.h b/turntablist/turntablist.h
index 35504926..13cb32ee 100644
--- a/turntablist/turntablist.h
+++ b/turntablist/turntablist.h
@@ -109,18 +109,6 @@ enum : dfx::ParameterID
kNumParameters
};
-enum : UInt32
-{
- kParamGroup_BaseID = kAudioUnitClumpID_System + 1,
- kParamGroup_Scratching = kParamGroup_BaseID,
- kParamGroup_Playback,
- kParamGroup_Power,
- kParamGroup_Pitch
-#ifdef INCLUDE_SILLY_OUTPUT_PARAMETERS
- , kParamGroup_Output
-#endif
-};
-
enum : dfx::PropertyID
{
kTurntablistProperty_Play = dfx::kPluginProperty_EndOfList,
@@ -159,6 +147,18 @@ class Turntablist final : public DfxPlugin
UInt32 inDesiredNameLength, CFStringRef* outClumpName) override;
private:
+ enum : UInt32
+ {
+ kParamGroup_BaseID = kAudioUnitClumpID_System + 1,
+ kParamGroup_Scratching = kParamGroup_BaseID,
+ kParamGroup_Playback,
+ kParamGroup_Power,
+ kParamGroup_Pitch
+ #ifdef INCLUDE_SILLY_OUTPUT_PARAMETERS
+ , kParamGroup_Output
+ #endif
+ };
+
OSStatus loadAudioFile(FSRef const& inFileRef);
OSStatus createAudioFileAlias(AliasHandle* outAlias, Size* outDataSize = nullptr);
OSStatus resolveAudioFileAlias(AliasHandle const inAlias);
@@ -179,11 +179,11 @@ class Turntablist final : public DfxPlugin
OSStatus PostNotification_AudioFileNotFound(CFStringRef inFileName);
- int m_nCurrentNote;
- int m_nCurrentVelocity;
- bool m_bNoteIsOn;
+ int m_nCurrentNote = 0;
+ int m_nCurrentVelocity = 0;
+ bool m_bNoteIsOn = false;
- FSRef m_fsAudioFile;
+ FSRef m_fsAudioFile {};
#ifdef USE_LIBSNDFILE
std::vector m_fBuffer;
@@ -194,98 +194,97 @@ class Turntablist final : public DfxPlugin
#endif
// our 32-bit floating point audio info
- int m_nNumChannels; // 1=mono, 2=stereo, 0=yomama
- int m_nNumSamples; // number of samples per channel
- int m_nSampleRate;
- double m_fSampleRate;
+ size_t m_nNumChannels = 0; // 1=mono, 2=stereo, 0=yomama
+ size_t m_nNumSamples = 0; // number of samples per channel
+ int m_nSampleRate = 0;
+ double m_fSampleRate = 0.;
// optional
- double m_fBasePitch;
- double m_fPlaySampleRate;
+ double m_fBasePitch = 0.;
+ double m_fPlaySampleRate = 0.;
// switches
- bool m_bPower; // on/off
- bool m_bNotePowerTrack; // scratch mode on/off
+ bool m_bPower = false; // on/off
+ bool m_bNotePowerTrack = false; // scratch mode on/off
// TODO: use dfx::SmoothedValue for continuous parameters
- double m_fScratchAmount;
- double m_fLastScratchAmount;
+ double m_fScratchAmount = 0.;
+ double m_fLastScratchAmount = 0.;
#ifdef INCLUDE_SILLY_OUTPUT_PARAMETERS
- bool m_bMute; // on/off
+ bool m_bMute = false; // on/off
#endif
- double m_fPitchShift;
- long m_nDirection;
+ double m_fPitchShift = 0.;
+ long m_nDirection = 0;
// float m_fScratchSpeed;
- double m_fScratchSpeed_scrub, m_fScratchSpeed_spin;
+ double m_fScratchSpeed_scrub = 0., m_fScratchSpeed_spin = 0.;
// modifiers
- long m_nNoteMode; // reset/resume
- bool m_bLoop; // on/off
- double m_fPitchRange;
- double m_fSpinDownSpeed;
- double m_fSpinUpSpeed;
- bool m_bKeyTracking;
+ long m_nNoteMode = 0; // reset/resume
+ bool m_bLoop = false; // on/off
+ double m_fPitchRange = 0.;
+ double m_fSpinDownSpeed = 0.;
+ double m_fSpinUpSpeed = 0.;
+ bool m_bKeyTracking = false;
#ifdef INCLUDE_SILLY_OUTPUT_PARAMETERS
- float m_fVolume;
+ float m_fVolume = 0.f;
#endif
- double m_fUsedSpinDownSpeed;
- double m_fUsedSpinUpSpeed;
+ double m_fUsedSpinDownSpeed = 0.;
+ double m_fUsedSpinUpSpeed = 0.;
- bool m_bPlayedReverse;
- int m_nRootKey;
+ bool m_bPlayedReverse = false;
+ int m_nRootKey = 0;
- double m_fPosition;
- double m_fPosOffset;
- double m_fNumSamples;
+ double m_fPosition = 0.;
+ double m_fPosOffset = 0.;
+ double m_fNumSamples = 0.;
- bool m_bPlay;
+ bool m_bPlay = false;
- bool m_bPitchBendSet;
- double m_fPitchBend;
- int m_nScratchDir;
- bool m_bScratching;
- bool m_bWasScratching;
- int m_nWasScratchingDir;
- int m_nScratchDelay;
+ bool m_bPitchBendSet = false;
+ double m_fPitchBend = 0.;
+ int m_nScratchDir = 0;
+ bool m_bScratching = false;
+ bool m_bWasScratching = false;
+ int m_nWasScratchingDir = 0;
- double m_fDesiredPitch;
+ double m_fDesiredPitch = 0.;
- long m_nScratchMode;
- long m_nScratchSubMode;
+ long m_nScratchMode = 0;
+ long m_nScratchSubMode = 0;
- float m_fNoteVolume; // recalculated on note on and volume changes
+ float m_fNoteVolume = 0.f; // recalculated on note on and volume changes
- int m_nPowerIntervalEnd;
+ int m_nPowerIntervalEnd = 0;
- bool m_bPlayForward;
+ bool m_bPlayForward = true;
- double m_fDesiredScratchRate;
- int m_nScratchInterval;
- int m_nScratchIntervalEnd;
- int m_nScratchIntervalEndBase;
- bool m_bScratchStop;
+ double m_fDesiredScratchRate = 0.;
+ int m_nScratchInterval = 0;
+ int m_nScratchIntervalEnd = 0;
+ int m_nScratchIntervalEndBase = 0;
+ bool m_bScratchStop = false;
- int m_nScratchCenter; // center position where scratching starts
- double m_fScratchCenter;
- double m_fDesiredPosition;
- double m_fPrevDesiredPosition;
+ int m_nScratchCenter = 0; // center position where scratching starts
+ double m_fScratchCenter = 0.;
+ double m_fDesiredPosition = 0.;
+ double m_fPrevDesiredPosition = 0.;
- float m_fScratchVolumeModifier;
+ float m_fScratchVolumeModifier = 0.f;
- bool m_bScratchAmountSet;
+ bool m_bScratchAmountSet = false;
- double m_fDesiredScratchRate2;
+ double m_fDesiredScratchRate2 = 0.;
- bool m_bAudioFileHasBeenLoaded;
+ bool m_bAudioFileHasBeenLoaded = false;
- double m_fTinyScratchAdjust;
+ double m_fTinyScratchAdjust = 0.;
// new power variables to do sample accurate powering up/down
-// double m_fDesiredPowerRate;
-// double m_fTinyPowerAdjust;
+// double m_fDesiredPowerRate = 0.;
+// double m_fTinyPowerAdjust = 0.;
std::mutex m_AudioFileLock;
};
From 928e674b7150e548e33a11796bd99588d3b8f9ef Mon Sep 17 00:00:00 2001
From: Sophia Poirier <2997196+sophiapoirier@users.noreply.github.com>
Date: Sun, 2 Oct 2022 20:04:06 -0700
Subject: [PATCH 3/4] use size_t for TempoRateTable indices
---
bufferoverride/bufferoverrideformalities.cpp | 10 +++++-----
dfx-library/dfxplugin.cpp | 7 +++++++
dfx-library/dfxplugin.h | 1 +
dfx-library/temporatetable.cpp | 8 ++++----
dfx-library/temporatetable.h | 20 ++++++++++----------
eqsync/eqsync.cpp | 4 ++--
scrubby/scrubby.h | 2 +-
scrubby/scrubbyformalities.cpp | 6 +++---
skidder/skidder.h | 2 +-
skidder/skidderformalities.cpp | 6 +++---
thrush/thrush.cpp | 6 +++---
11 files changed, 40 insertions(+), 32 deletions(-)
diff --git a/bufferoverride/bufferoverrideformalities.cpp b/bufferoverride/bufferoverrideformalities.cpp
index 07ab5969..c789495b 100644
--- a/bufferoverride/bufferoverrideformalities.cpp
+++ b/bufferoverride/bufferoverrideformalities.cpp
@@ -72,7 +72,7 @@ BufferOverride::BufferOverride(TARGET_API_BASE_INSTANCE_TYPE inInstance)
setparametervaluestring(kBufferLFOShape, i, shapeName);
}
// set the value strings for the sync rate parameters
- for (long i = 0; i < mTempoRateTable.getNumRates(); i++)
+ for (size_t i = 0; i < numTempoRates; i++)
{
auto const& tempoRateName = mTempoRateTable.getDisplay(i);
setparametervaluestring(kBufferSize_Sync, i, tempoRateName);
@@ -339,16 +339,16 @@ void BufferOverride::processparameters()
{
mDivisor = getparameter_f(kDivisor);
mBufferSizeMS = getparameter_f(kBufferSize_MS);
- mBufferSizeSync = mTempoRateTable.getScalar(getparameter_i(kBufferSize_Sync));
+ mBufferSizeSync = mTempoRateTable.getScalar(getparameter_index(kBufferSize_Sync));
mBufferTempoSync = getparameter_b(kBufferTempoSync);
mBufferInterrupt = getparameter_b(kBufferInterrupt);
mDivisorLFORateHz = getparameter_f(kDivisorLFORate_Hz);
- mDivisorLFOTempoRate = mTempoRateTable.getScalar(getparameter_i(kDivisorLFORate_Sync));
+ mDivisorLFOTempoRate = mTempoRateTable.getScalar(getparameter_index(kDivisorLFORate_Sync));
mDivisorLFO.setDepth(getparameter_scalar(kDivisorLFODepth));
mDivisorLFO.setShape(getparameter_i(kDivisorLFOShape));
mDivisorLFOTempoSync = getparameter_b(kDivisorLFOTempoSync);
mBufferLFORateHz = getparameter_f(kBufferLFORate_Hz);
- mBufferLFOTempoRate = mTempoRateTable.getScalar(getparameter_i(kBufferLFORate_Sync));
+ mBufferLFOTempoRate = mTempoRateTable.getScalar(getparameter_index(kBufferLFORate_Sync));
mBufferLFO.setDepth(getparameter_scalar(kBufferLFODepth));
mBufferLFO.setShape(getparameter_i(kBufferLFOShape));
mBufferLFOTempoSync = getparameter_b(kBufferLFOTempoSync);
@@ -424,7 +424,7 @@ void BufferOverride::updateViewDataCache()
if (getparameter_b(kBufferTempoSync) && (tempoBPS > 0.))
{
- viewData.mPreLFO.mForcedBufferSeconds = 1. / (tempoBPS * mTempoRateTable.getScalar(getparameter_i(kBufferSize_Sync)));
+ viewData.mPreLFO.mForcedBufferSeconds = 1. / (tempoBPS * mTempoRateTable.getScalar(getparameter_index(kBufferSize_Sync)));
}
else
{
diff --git a/dfx-library/dfxplugin.cpp b/dfx-library/dfxplugin.cpp
index 9de35edc..e3cd1234 100644
--- a/dfx-library/dfxplugin.cpp
+++ b/dfx-library/dfxplugin.cpp
@@ -689,6 +689,13 @@ DfxParam::Value DfxPlugin::getparameter(dfx::ParameterID inParameterID) const
return {};
}
+size_t DfxPlugin::getparameter_index(dfx::ParameterID inParameterID) const
+{
+ auto const value = getparameter_i(inParameterID);
+ assert(value >= 0);
+ return dfx::math::ToIndex(value);
+}
+
//-----------------------------------------------------------------------------
// return a (hopefully) 0 to 1 scalar version of the parameter's current value
double DfxPlugin::getparameter_scalar(dfx::ParameterID inParameterID) const
diff --git a/dfx-library/dfxplugin.h b/dfx-library/dfxplugin.h
index bc11fb37..ce461ada 100644
--- a/dfx-library/dfxplugin.h
+++ b/dfx-library/dfxplugin.h
@@ -353,6 +353,7 @@ class DfxPlugin : public TARGET_API_BASE_CLASS
{
return parameterisvalid(inParameterID) ? mParameters[inParameterID].get_gen() : 0.0;
}
+ size_t getparameter_index(dfx::ParameterID inParameterID) const;
// return a (hopefully) 0 to 1 scalar version of the parameter's current value
double getparameter_scalar(dfx::ParameterID inParameterID) const;
std::optional getparameterifchanged_f(dfx::ParameterID inParameterID) const;
diff --git a/dfx-library/temporatetable.cpp b/dfx-library/temporatetable.cpp
index 6cb75493..a19a862e 100644
--- a/dfx-library/temporatetable.cpp
+++ b/dfx-library/temporatetable.cpp
@@ -18,7 +18,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Destroy FX Library. If not, see .
-To contact the author, use the contact form at http://destroyfx.org/
+To contact the author, use the contact form at http://destroyfx.org
Destroy FX is a sovereign entity comprised of Sophia Poirier and Tom Murphy 7.
Welcome to our tempo rate table.
@@ -91,11 +91,11 @@ dfx::TempoRateTable::TempoRateTable(Rates inRates)
//-----------------------------------------------------------------------------
// given a tempo rate value, return the index of the tempo rate
// that is closest to that requested value
-long dfx::TempoRateTable::getNearestTempoRateIndex(double inTempoRateValue) const
+size_t dfx::TempoRateTable::getNearestTempoRateIndex(double inTempoRateValue) const
{
auto bestDiff = mScalars.back();
- long bestIndex = 0;
- for (long i = 0; i < getNumRates(); i++)
+ size_t bestIndex = 0;
+ for (size_t i = 0; i < getNumRates(); i++)
{
auto const diff = std::fabs(inTempoRateValue - mScalars[i]);
if (diff < bestDiff)
diff --git a/dfx-library/temporatetable.h b/dfx-library/temporatetable.h
index 6dda648a..d157e64a 100644
--- a/dfx-library/temporatetable.h
+++ b/dfx-library/temporatetable.h
@@ -18,7 +18,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Destroy FX Library. If not, see .
-To contact the author, use the contact form at http://destroyfx.org/
+To contact the author, use the contact form at http://destroyfx.org
Destroy FX is a sovereign entity comprised of Sophia Poirier and Tom Murphy 7.
Welcome to our tempo rate table.
@@ -50,24 +50,24 @@ class TempoRateTable
explicit TempoRateTable(Rates inRates = Rates::Normal);
- double getScalar(long inIndex) const
+ double getScalar(size_t inIndex) const
{
- return mScalars[safeIndex(inIndex)];
+ return mScalars[boundedIndex(inIndex)];
}
- std::string const& getDisplay(long inIndex) const
+ std::string const& getDisplay(size_t inIndex) const
{
- return mDisplays[safeIndex(inIndex)];
+ return mDisplays[boundedIndex(inIndex)];
}
- long getNumRates() const noexcept
+ auto getNumRates() const noexcept
{
- return static_cast(mScalars.size());
+ return mScalars.size();
}
- long getNearestTempoRateIndex(double inTempoRateValue) const;
+ size_t getNearestTempoRateIndex(double inTempoRateValue) const;
private:
- size_t safeIndex(long inIndex) const noexcept
+ size_t boundedIndex(size_t inIndex) const noexcept
{
- return static_cast(std::clamp(inIndex, 0L, getNumRates() - 1));
+ return std::min(inIndex, getNumRates() - 1);
}
std::vector mScalars;
diff --git a/eqsync/eqsync.cpp b/eqsync/eqsync.cpp
index ab6d5aef..69763b85 100644
--- a/eqsync/eqsync.cpp
+++ b/eqsync/eqsync.cpp
@@ -53,7 +53,7 @@ EQSync::EQSync(TARGET_API_BASE_INSTANCE_TYPE inInstance)
initparameter_f(kB2, {"b2"}, 0.5, 0.5, 0.0, 1.0, DfxParam::Unit::Generic);
// set the value strings for the sync rate parameters
- for (long i = 0; i < numTempoRates; i++)
+ for (size_t i = 0; i < numTempoRates; i++)
{
setparametervaluestring(kRate_Sync, i, mTempoRateTable.getDisplay(i));
}
@@ -107,7 +107,7 @@ void EQSync::reset()
//-----------------------------------------------------------------------------
void EQSync::processparameters()
{
- mRate = mTempoRateTable.getScalar(getparameter_i(kRate_Sync));
+ mRate = mTempoRateTable.getScalar(getparameter_index(kRate_Sync));
mSmooth = getparameter_scalar(kSmooth);
mUserTempo = getparameter_f(kTempo);
mUseHostTempo = getparameter_b(kTempoAuto);
diff --git a/scrubby/scrubby.h b/scrubby/scrubby.h
index a092382a..b96c042f 100644
--- a/scrubby/scrubby.h
+++ b/scrubby/scrubby.h
@@ -129,7 +129,7 @@ class Scrubby final : public DfxPlugin
// the parameters
double mSeekRangeSeconds = 0.0, mSeekDur = 0.0, mSeekDurRandMin = 0.0;
double mSeekRateHz = 0.0, mSeekRateSync = 0.0;
- long mSeekRateIndex = 0, mSeekRateRandMinIndex = 0;
+ size_t mSeekRateIndex = 0, mSeekRateRandMinIndex = 0;
double mUserTempo = 0.0;
long mSpeedMode = kSpeedMode_Robot, mOctaveMin = 0, mOctaveMax = 0;
bool mFreeze = false, mSplitChannels = false, mPitchConstraint = false, mTempoSync = false, mUseHostTempo = false;
diff --git a/scrubby/scrubbyformalities.cpp b/scrubby/scrubbyformalities.cpp
index c6a7bb24..d721b98d 100644
--- a/scrubby/scrubbyformalities.cpp
+++ b/scrubby/scrubbyformalities.cpp
@@ -81,7 +81,7 @@ Scrubby::Scrubby(TARGET_API_BASE_INSTANCE_TYPE inInstance)
setparameterenforcevaluelimits(kPredelay, true);
// set the value strings for the sync rate parameters
- for (long i = 0; i < numTempoRates; i++)
+ for (size_t i = 0; i < numTempoRates; i++)
{
auto const& tempoRateName = mTempoRateTable.getDisplay(i);
setparametervaluestring(kSeekRate_Sync, i, tempoRateName);
@@ -401,10 +401,10 @@ void Scrubby::processparameters()
mSeekRangeSeconds = getparameter_f(kSeekRange) * 0.001;
mFreeze = getparameter_b(kFreeze);
mSeekRateHz = getparameter_f(kSeekRate_Hz);
- mSeekRateIndex = getparameter_i(kSeekRate_Sync);
+ mSeekRateIndex = getparameter_index(kSeekRate_Sync);
mSeekRateSync = mTempoRateTable.getScalar(mSeekRateIndex);
auto const seekRateRandMinHz = getparameter_f(kSeekRateRandMin_Hz);
- mSeekRateRandMinIndex = getparameter_i(kSeekRateRandMin_Sync);
+ mSeekRateRandMinIndex = getparameter_index(kSeekRateRandMin_Sync);
auto const seekRateRandMinSync = mTempoRateTable.getScalar(mSeekRateRandMinIndex);
mTempoSync = getparameter_b(kTempoSync);
mSeekDur = getparameter_scalar(kSeekDur);
diff --git a/skidder/skidder.h b/skidder/skidder.h
index 7afc7c75..e8970e11 100644
--- a/skidder/skidder.h
+++ b/skidder/skidder.h
@@ -135,7 +135,7 @@ class Skidder final : public DfxPlugin
// the parameters
double mRate_Hz = 1., mRate_Sync = 1.;
float mPulsewidth = 0.f, mPulsewidthRandMin = 0.f;
- long mRateIndex = 0, mRateRandMinIndex = 0;
+ size_t mRateIndex = 0, mRateRandMinIndex = 0;
float mPanWidth = 0.0f, mFloor = 0.0f;
dfx::SmoothedValue mNoise;
double mSlopeSeconds = 0., mUserTempo = 1.;
diff --git a/skidder/skidderformalities.cpp b/skidder/skidderformalities.cpp
index 39623ae0..f1a6a467 100644
--- a/skidder/skidderformalities.cpp
+++ b/skidder/skidderformalities.cpp
@@ -59,7 +59,7 @@ Skidder::Skidder(TARGET_API_BASE_INSTANCE_TYPE inInstance)
setparameterenforcevaluelimits(kCrossoverFrequency, true);
// set the value strings for the sync rate parameters
- for (long i = 0; i < mTempoRateTable.getNumRates(); i++)
+ for (size_t i = 0; i < numTempoRates; i++)
{
auto const& tempoRateName = mTempoRateTable.getDisplay(i);
setparametervaluestring(kRate_Sync, i, tempoRateName);
@@ -152,10 +152,10 @@ void Skidder::reset()
void Skidder::processparameters()
{
mRate_Hz = getparameter_f(kRate_Hz);
- mRateIndex = getparameter_i(kRate_Sync);
+ mRateIndex = getparameter_index(kRate_Sync);
mRate_Sync = mTempoRateTable.getScalar(mRateIndex);
auto const rateRandMin_Hz = getparameter_f(kRateRandMin_Hz);
- mRateRandMinIndex = getparameter_i(kRateRandMin_Sync);
+ mRateRandMinIndex = getparameter_index(kRateRandMin_Sync);
auto const rateRandMin_Sync = mTempoRateTable.getScalar(mRateRandMinIndex);
mTempoSync = getparameter_b(kTempoSync);
mPulsewidth = getparameter_f(kPulsewidth);
diff --git a/thrush/thrush.cpp b/thrush/thrush.cpp
index 11c6f764..8e3e59f2 100644
--- a/thrush/thrush.cpp
+++ b/thrush/thrush.cpp
@@ -78,7 +78,7 @@ Thrush::Thrush(TARGET_API_BASE_INSTANCE_TYPE inInstance)
setparametervaluestring(kLFO2Shape2, i, shapeName);
}
// set the value strings for the sync rate parameters
- for (long i = 0; i < mTempoRateTable.getNumRates(); i++)
+ for (size_t i = 0; i < numTempoRates; i++)
{
auto const& tempoRateName = mTempoRateTable.getDisplay(i);
setparametervaluestring(kLFO1Rate_Sync, i, tempoRateName);
@@ -284,7 +284,7 @@ void Thrush::processparameters()
mDelay2_gen = getparameter_gen(kDelay2);
mLFO1_2.mRateHz = getparameter_f(kLFO1Rate2_Hz);
- mLFO1_2.mTempoRateScalar = mTempoRateTable.getScalar(getparameter_i(kLFO1Rate2_Sync));
+ mLFO1_2.mTempoRateScalar = mTempoRateTable.getScalar(getparameter_index(kLFO1Rate2_Sync));
if (auto const value = getparameterifchanged_b(kLFO1TempoSync2))
{
@@ -296,7 +296,7 @@ void Thrush::processparameters()
mLFO1_2.setDepth(getparameter_scalar(kLFO1Depth2));
mLFO1_2.setShape(getparameter_i(kLFO1Shape2));
mLFO2_2.mRateHz = getparameter_f(kLFO2Rate2_Hz);
- mLFO2_2.mTempoRateScalar = mTempoRateTable.getScalar(getparameter_i(kLFO2Rate2_Sync));
+ mLFO2_2.mTempoRateScalar = mTempoRateTable.getScalar(getparameter_index(kLFO2Rate2_Sync));
if (auto const value = getparameterifchanged_b(kLFO2TempoSync2))
{
From fe57c7feecfb0c878e971ba6b7618c5313a1911e Mon Sep 17 00:00:00 2001
From: Sophia Poirier <2997196+sophiapoirier@users.noreply.github.com>
Date: Sat, 19 Feb 2022 16:28:32 -0800
Subject: [PATCH 4/4] Transverb: various approaches to adapt to "distance"
changes without crackly glitches
---
transverb/transverb-base.h | 3 ++
transverb/transverb.h | 18 ++++++-
transverb/transverbformalities.cpp | 81 +++++++++++++++++++++++++++---
transverb/transverbprocess.cpp | 66 ++++++++++++++++--------
4 files changed, 137 insertions(+), 31 deletions(-)
diff --git a/transverb/transverb-base.h b/transverb/transverb-base.h
index 941b3203..70729c24 100644
--- a/transverb/transverb-base.h
+++ b/transverb/transverb-base.h
@@ -48,6 +48,7 @@ enum : dfx::ParameterID
kTomsound,
kFreeze,
kAttenuateFeedbackByMixLevel,
+ kDistChangeMode,
kNumParameters
};
@@ -66,6 +67,8 @@ enum { kQualityMode_DirtFi, kQualityMode_HiFi, kQualityMode_UltraHiFi, kQualityM
enum : unsigned int { kSpeedMode_Fine, kSpeedMode_Semitone, kSpeedMode_Octave, kSpeedMode_NumModes };
static constexpr dfx::PropertyID kTransverbProperty_SpeedModeBase = dfx::kPluginProperty_EndOfList;
+enum { kDistChangeMode_Reverse, kDistChangeMode_AdHocVarispeed, kDistChangeMode_DistanceVarispeed, kDistChangeMode_BufferVarispeed, kDistChangeMode_LoopingBufferVarispeed, kDistChangeMode_Count };
+
dfx::PropertyID speedModeIndexToPropertyID(size_t inIndex) noexcept;
size_t speedModePropertyIDToIndex(dfx::PropertyID inPropertyID) noexcept;
diff --git a/transverb/transverb.h b/transverb/transverb.h
index a1987b4c..71525676 100644
--- a/transverb/transverb.h
+++ b/transverb/transverb.h
@@ -26,6 +26,7 @@ To contact the author, use the contact form at http://destroyfx.org
#include
#include
#include
+#include
#include
#include "dfxplugin.h"
@@ -57,6 +58,8 @@ class TransverbDSP final : public DfxPluginCore {
dfx::SmoothedValue mix, feed;
double read = 0.;
+ std::optional targetdist;
+ double distspeedfactor = 1.;
std::vector buf;
dfx::IIRFilter filter;
@@ -83,17 +86,23 @@ class TransverbDSP final : public DfxPluginCore {
static constexpr int mod_bipolar(int value, int modulo);
static inline double fmod_bipolar(double value, double modulo);
+ // distance in samples of a read position from the current write position
+ double getdist(double read) const;
+ void processdist(double distnormalized, Head& head);
+
// these store the parameter values
int bsize = 0;
dfx::SmoothedValue drymix;
- long quality = 0;
- bool tomsound = false;
+ int distchangemode {};
int writer = 0;
std::array heads;
int const MAXBUF; // the size of the audio buffer (dependent on sampling rate)
+ bool firstrendersincereset = false;
+ std::vector& buftemp;
+
std::vector const firCoefficientsWindow;
};
@@ -116,6 +125,10 @@ class Transverb final : public DfxPlugin {
dfx::StatusCode dfx_SetProperty(dfx::PropertyID inPropertyID, dfx::Scope inScope, unsigned int inItemIndex,
void const* inData, size_t inDataSize) override;
+ auto& getscratchbuffer() noexcept {
+ return buftemp;
+ }
+
protected:
size_t settings_sizeOfExtendedData() const noexcept override;
void settings_saveExtendedData(void* outData, bool isPreset) override;
@@ -132,6 +145,7 @@ class Transverb final : public DfxPlugin {
}
std::array speedModeStates {};
+ std::vector buftemp; // shared between all DSP cores (memory optimization)
};
diff --git a/transverb/transverbformalities.cpp b/transverb/transverbformalities.cpp
index 0d493b63..654c9b8f 100644
--- a/transverb/transverbformalities.cpp
+++ b/transverb/transverbformalities.cpp
@@ -57,6 +57,7 @@ Transverb::Transverb(TARGET_API_BASE_INSTANCE_TYPE inInstance)
initparameter_b(kTomsound, {"TOMSOUND", "TomSnd", "Tom7"}, false);
initparameter_b(kFreeze, dfx::MakeParameterNames(dfx::kParameterNames_Freeze), false);
initparameter_b(kAttenuateFeedbackByMixLevel, {"attenuate feedback by mix level", "AtnFdbk", "AtnFdb", "-fdb"}, false);
+ initparameter_list(kDistChangeMode, {"distance change mode", "DistMod", "DstMod", "DMod"}, kDistChangeMode_Reverse, kDistChangeMode_Reverse, kDistChangeMode_Count);
setparameterenforcevaluelimits(kBsize, true);
for (auto const parameterID : kDistParameters) {
@@ -67,10 +68,12 @@ Transverb::Transverb(TARGET_API_BASE_INSTANCE_TYPE inInstance)
setparametervaluestring(kQuality, kQualityMode_HiFi, "hi-fi");
setparametervaluestring(kQuality, kQualityMode_UltraHiFi, "ultra hi-fi");
- // distance parameters only have meaningful effect at zero speed which probably will never occur randomly,
- // and otherwise all they do is glitch a lot, so omit them
- addparameterattributes(kDist1, DfxParam::kAttribute_OmitFromRandomizeAll);
- addparameterattributes(kDist2, DfxParam::kAttribute_OmitFromRandomizeAll);
+ setparametervaluestring(kDistChangeMode, kDistChangeMode_Reverse, "reverse");
+ setparametervaluestring(kDistChangeMode, kDistChangeMode_AdHocVarispeed, "ad hoc varispeed");
+ setparametervaluestring(kDistChangeMode, kDistChangeMode_DistanceVarispeed, "distance varispeed");
+ setparametervaluestring(kDistChangeMode, kDistChangeMode_BufferVarispeed, "buffer varispeed");
+ setparametervaluestring(kDistChangeMode, kDistChangeMode_LoopingBufferVarispeed, "looping buffer varispeed");
+
addparameterattributes(kFreeze, DfxParam::kAttribute_OmitFromRandomizeAll);
addparameterattributes(kAttenuateFeedbackByMixLevel, DfxParam::kAttribute_OmitFromRandomizeAll);
@@ -109,8 +112,11 @@ void Transverb::dfx_PostConstructor() {
TransverbDSP::TransverbDSP(DfxPlugin* inDfxPlugin)
: DfxPluginCore(inDfxPlugin),
MAXBUF(static_cast(getparametermax_f(kBsize) * 0.001 * getsamplerate())),
+ buftemp(dynamic_cast(inDfxPlugin)->getscratchbuffer()),
firCoefficientsWindow(dfx::FIRFilter::generateKaiserWindow(kNumFIRTaps, 60.0f)) {
+ buftemp.assign(MAXBUF, 0.f);
+
registerSmoothedAudioValue(&drymix);
for (auto& head : heads) {
@@ -126,12 +132,15 @@ TransverbDSP::TransverbDSP(DfxPlugin* inDfxPlugin)
void TransverbDSP::reset() {
std::for_each(heads.begin(), heads.end(), [](Head& head){ head.reset(); });
+
+ firstrendersincereset = true;
}
void TransverbDSP::Head::reset() {
smoothcount = 0;
lastdelayval = 0.f;
+ targetdist.reset();
filter.reset();
speedHasChanged = true;
@@ -164,8 +173,6 @@ void TransverbDSP::processparameters() {
heads[head].feed = *value;
}
}
- quality = getparameter_i(kQuality);
- tomsound = getparameter_b(kTomsound);
if (auto const value = getparameterifchanged_f(kBsize))
{
@@ -193,12 +200,12 @@ void TransverbDSP::processparameters() {
std::for_each(heads.begin(), heads.end(), [bsize_f](Head& head){ head.read = fmod_bipolar(head.read, bsize_f); });
}
+ distchangemode = getparameter_i(kDistChangeMode);
for (size_t head = 0; head < kNumDelays; head++)
{
if (auto const dist = getparameterifchanged_f(kDistParameters[head]))
{
- auto const bsize_f = static_cast(bsize);
- heads[head].read = fmod_bipolar(static_cast(writer) - (*dist * bsize_f), bsize_f);
+ processdist(*dist, heads[head]);
}
}
@@ -221,6 +228,64 @@ void TransverbDSP::processparameters() {
heads[0].mix.setValueNow(getparametermin_f(kMix1));
}
}
+
+ firstrendersincereset = false;
+}
+
+double TransverbDSP::getdist(double read) const {
+
+ return fmod_bipolar(static_cast(writer) - read, static_cast(bsize));
+}
+
+void TransverbDSP::processdist(double distnormalized, Head& head) {
+
+ auto const bsize_f = static_cast(bsize);
+ auto const distsamples = distnormalized * bsize_f;
+ auto const targetread = fmod_bipolar(static_cast(writer) - distsamples, bsize_f);
+
+ if (firstrendersincereset)
+ {
+ head.read = targetread;
+ }
+ else
+ {
+ if (distchangemode == kDistChangeMode_Reverse)
+ {
+ head.targetdist = distsamples;
+ }
+ else if (auto const resamplerate = getdist(head.read) / std::max(distsamples, 1.); distchangemode == kDistChangeMode_AdHocVarispeed)
+ {
+ head.targetdist = distsamples;
+ head.distspeedfactor = resamplerate;
+ head.speedHasChanged = true;
+ }
+ else
+ {
+ constexpr double modulationthresholdsamples = 1.;
+ if (std::fabs(targetread - head.read) >= modulationthresholdsamples)
+ {
+ auto const bsizesteps = bsize_f / resamplerate;
+ bool const subslice = (distchangemode == kDistChangeMode_DistanceVarispeed);
+ bool const looping = (distchangemode == kDistChangeMode_LoopingBufferVarispeed);
+ auto const copylength_f = subslice ? distsamples : (looping ? bsize_f : std::min(bsize_f, bsizesteps));
+ auto const copylength = static_cast(std::lround(copylength_f));
+ assert(copylength <= buftemp.size());
+ assert(static_cast(copylength) <= bsize);
+ auto const sourcestart = subslice ? head.read : fmod_bipolar(static_cast(writer) - (copylength_f * resamplerate), bsize_f);
+ for (size_t i = 0; i < copylength; i++)
+ {
+ auto const sourcepos = std::fmod(sourcestart + (resamplerate * static_cast(i)), bsize_f);
+ buftemp[i] = interpolateHermite(head.buf.data(), sourcepos, bsize, writer);
+ }
+ auto const destinationstart = subslice ? std::lround(targetread) : mod_bipolar(writer - static_cast(copylength), bsize);
+ auto const copylength1 = std::min(static_cast(bsize - destinationstart), copylength);
+ auto const copylength2 = copylength - copylength1;
+ std::copy_n(buftemp.cbegin(), copylength1, std::next(head.buf.begin(), destinationstart));
+ std::copy_n(std::next(buftemp.cbegin(), copylength1), copylength2, head.buf.begin());
+ }
+ head.read = targetread;
+ }
+ }
}
diff --git a/transverb/transverbprocess.cpp b/transverb/transverbprocess.cpp
index 7f2f06c8..be0a50af 100644
--- a/transverb/transverbprocess.cpp
+++ b/transverb/transverbprocess.cpp
@@ -36,11 +36,13 @@ using namespace dfx::TV;
void TransverbDSP::process(float const* inAudio, float* outAudio, size_t numSampleFrames) {
- std::array delayvals {}; // delay buffer output values
auto const bsize_float = static_cast(bsize); // cut down on casting
+ auto const quality = getparameter_i(kQuality);
+ auto const tomsound = getparameter_b(kTomsound);
auto const freeze = getparameter_b(kFreeze);
int const writerIncrement = freeze ? 0 : 1;
auto const attenuateFeedbackByMixLevel = getparameter_b(kAttenuateFeedbackByMixLevel);
+ std::array delayvals {}; // delay buffer output values
///////////// S O P H I A S O U N D //////////////
@@ -69,6 +71,9 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, size_t numSamp
for (size_t h = 0; h < kNumDelays; h++) // delay heads loop
{
auto const read_int = static_cast(heads[h].read);
+ auto const reverseread = (distchangemode == kDistChangeMode_Reverse) && heads[h].targetdist.has_value();
+ auto const distcatchup = (distchangemode == kDistChangeMode_AdHocVarispeed) && heads[h].targetdist.has_value();
+ auto const speed = heads[h].speed.getValue() * (distcatchup ? heads[h].distspeedfactor : 1.);
// filter setup
if (quality == kQualityMode_UltraHiFi)
@@ -77,27 +82,27 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, size_t numSamp
{
lowpasspos[h] = read_int;
// check to see if we need to lowpass the first delay head and init coefficients if so
- if (heads[h].speed.getValue() > kUnitySpeed)
+ if (speed > kUnitySpeed)
{
filtermodes[h] = FilterMode::LowpassIIR;
- speed_ints[h] = static_cast(heads[h].speed.getValue());
+ speed_ints[h] = static_cast(speed);
// it becomes too costly to try to IIR at higher speeds, so switch to FIR filtering
- if (heads[h].speed.getValue() >= kFIRSpeedThreshold)
+ if (speed >= kFIRSpeedThreshold)
{
filtermodes[h] = FilterMode::LowpassFIR;
// compensate for gain lost from filtering
- mugs[h] = static_cast(std::pow(heads[h].speed.getValue() / kFIRSpeedThreshold, 0.78));
+ mugs[h] = static_cast(std::pow(speed / kFIRSpeedThreshold, 0.78));
// update the coefficients only if necessary
if (std::exchange(heads[h].speedHasChanged, false))
{
- dfx::FIRFilter::calculateIdealLowpassCoefficients((samplerate / heads[h].speed.getValue()) * dfx::FIRFilter::kShelfStartLowpass,
+ dfx::FIRFilter::calculateIdealLowpassCoefficients((samplerate / speed) * dfx::FIRFilter::kShelfStartLowpass,
samplerate, heads[h].firCoefficients, firCoefficientsWindow);
heads[h].filter.reset();
}
}
else if (std::exchange(heads[h].speedHasChanged, false))
{
- heads[h].filter.setLowpassCoefficients((samplerate / heads[h].speed.getValue()) * dfx::IIRFilter::kShelfStartLowpass);
+ heads[h].filter.setLowpassCoefficients((samplerate / speed) * dfx::IIRFilter::kShelfStartLowpass);
}
}
// we need to highpass the delay head to remove mega sub bass
@@ -106,7 +111,7 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, size_t numSamp
filtermodes[h] = FilterMode::Highpass;
if (std::exchange(heads[h].speedHasChanged, false))
{
- heads[h].filter.setHighpassCoefficients(kHighpassFilterCutoff / heads[h].speed.getValue());
+ heads[h].filter.setHighpassCoefficients(kHighpassFilterCutoff / speed);
}
}
}
@@ -173,18 +178,18 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, size_t numSamp
// start smoothing if the writer has passed a reader or vice versa,
// though not if reader and writer move at the same speed
// (check the positions before wrapping around the heads)
- auto const nextRead = static_cast(heads[h].read + heads[h].speed.getValue());
+ auto const nextRead = static_cast(heads[h].read + speed);
auto const nextWrite = writer + 1;
bool const readCrossingAhead = (read_int < writer) && (nextRead >= nextWrite);
bool const readCrossingBehind = (read_int >= writer) && (nextRead <= nextWrite);
- bool const speedIsUnity = heads[h].speed.getValue() == kUnitySpeed;
+ bool const speedIsUnity = (speed == kUnitySpeed);
if ((readCrossingAhead || readCrossingBehind) && !speedIsUnity) {
// check because, at slow speeds, it's possible to go into this twice or more in a row
if (heads[h].smoothcount <= 0) {
// store the most recent output as the head's smoothing sample
heads[h].lastdelayval = delayvals[h];
// truncate the smoothing duration if we're using too small of a buffer size
- auto const bufferReadSteps = static_cast(bsize_float / heads[h].speed.getValue());
+ auto const bufferReadSteps = static_cast(bsize_float / speed);
auto const smoothdur = std::min(bufferReadSteps, kAudioSmoothingDur_samples);
heads[h].smoothstep = 1.f / static_cast(smoothdur); // the scalar step value
heads[h].smoothcount = smoothdur; // set the counter to the total duration
@@ -192,9 +197,21 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, size_t numSamp
}
// update read heads, wrapping around if they have gone past the end of the buffer
- heads[h].read += heads[h].speed.getValue();
- if (heads[h].read >= bsize_float) {
- heads[h].read = fmod_bipolar(heads[h].read, bsize_float);
+ if (reverseread) {
+ heads[h].read -= speed;
+ while (heads[h].read < 0.) {
+ heads[h].read += bsize_float;
+ }
+ } else {
+ heads[h].read += speed;
+ if (heads[h].read >= bsize_float) {
+ heads[h].read = fmod_bipolar(heads[h].read, bsize_float);
+ }
+ }
+ if (distcatchup || reverseread) {
+ if (std::fabs(getdist(heads[h].read) - *heads[h].targetdist) < speed) {
+ heads[h].targetdist.reset();
+ }
}
// if we're doing IIR lowpass filtering,
@@ -204,40 +221,47 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, size_t numSamp
if (filtermodes[h] == FilterMode::LowpassIIR)
{
int lowpasscount = 0;
+ int const direction = reverseread ? -1 : 1;
while (lowpasscount < speed_ints[h])
{
switch (speed_ints[h] - lowpasscount)
{
case 1:
heads[h].filter.processToCacheH1(heads[h].buf[lowpasspos[h]]);
- lowpasspos[h] = (lowpasspos[h] + 1) % bsize;
+ lowpasspos[h] = mod_bipolar(lowpasspos[h] + (1 * direction), bsize);
lowpasscount++;
break;
case 2:
heads[h].filter.processToCacheH2(heads[h].buf.data(), dfx::math::ToUnsigned(lowpasspos[h]), dfx::math::ToUnsigned(bsize));
- lowpasspos[h] = (lowpasspos[h] + 2) % bsize;
+ lowpasspos[h] = mod_bipolar(lowpasspos[h] + (2 * direction), bsize);
lowpasscount += 2;
break;
case 3:
heads[h].filter.processToCacheH3(heads[h].buf.data(), dfx::math::ToUnsigned(lowpasspos[h]), dfx::math::ToUnsigned(bsize));
- lowpasspos[h] = (lowpasspos[h] + 3) % bsize;
+ lowpasspos[h] = mod_bipolar(lowpasspos[h] + (3 * direction), bsize);
lowpasscount += 3;
break;
default:
heads[h].filter.processToCacheH4(heads[h].buf.data(), dfx::math::ToUnsigned(lowpasspos[h]), dfx::math::ToUnsigned(bsize));
- lowpasspos[h] = (lowpasspos[h] + 4) % bsize;
+ lowpasspos[h] = mod_bipolar(lowpasspos[h] + (4 * direction), bsize);
lowpasscount += 4;
break;
}
}
auto const nextread_int = static_cast(heads[h].read);
// check whether we need to consume one more sample
- bool const extrasample = ((lowpasspos[h] < nextread_int) && ((lowpasspos[h] + 1) == nextread_int)) ||
- ((lowpasspos[h] == (bsize - 1)) && (nextread_int == 0));
+ bool const extrasample = [=] {
+ if (reverseread) {
+ return ((lowpasspos[h] > nextread_int) && ((lowpasspos[h] - 1) == nextread_int)) ||
+ ((lowpasspos[h] == 0) && (nextread_int == (bsize - 1)));
+ }
+ return ((lowpasspos[h] < nextread_int) && ((lowpasspos[h] + 1) == nextread_int)) ||
+ ((lowpasspos[h] == (bsize - 1)) && (nextread_int == 0));
+ }();
if (extrasample)
{
heads[h].filter.processToCacheH1(heads[h].buf[lowpasspos[h]]);
- lowpasspos[h] = (lowpasspos[h] + 1) % bsize;
+ lowpasspos[h] = mod_bipolar(lowpasspos[h] + (1 * direction), bsize);
}
}
// it's simpler for highpassing;