Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transverb: adapt to "distance" changes without crackly glitches #63

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions transverb/gui/transverbeditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,12 @@ void TransverbEditor::OpenEditor()
SetParameterHelpText(kQuality, "level of transposition quality of the delays' speed");
SetParameterHelpText(kTomsound, "megaharsh sound");
SetParameterHelpText(kFreeze, "pause recording new audio into the delay buffer");


#if 1
pos.set(120, getFrame()->getHeight() - 24, 210, 16);
emplaceControl<DGPopUpMenu>(this, kDistChangeMode, pos, dfx::TextAlignment::Center, kDisplayTextSize, VSTGUI::MakeCColor(92, 151, 209), kDisplayFont);
#endif
}

//-----------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions transverb/transverb-base.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum : dfx::ParameterID
kTomsound,
kFreeze,
kAttenuateFeedbackByMixLevel,
kDistChangeMode,

kNumParameters
};
Expand All @@ -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;
Expand Down
18 changes: 16 additions & 2 deletions transverb/transverb.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ To contact the author, use the contact form at http:https://destroyfx.org
#include <cmath>
#include <cstdint>
#include <numeric>
#include <optional>
#include <span>
#include <vector>

Expand Down Expand Up @@ -59,6 +60,8 @@ class TransverbDSP final : public DfxPluginCore {
dfx::SmoothedValue<float> mix, feed;

double read = 0.;
std::optional<double> targetdist;
double distspeedfactor = 1.;
std::vector<float> buf;

dfx::IIRFilter filter;
Expand All @@ -85,17 +88,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<float> drymix;
long quality = 0;
bool tomsound = false;
int distchangemode {};

int writer = 0;
std::array<Head, dfx::TV::kNumDelays> heads;

int const MAXBUF; // the size of the audio buffer (dependent on sampling rate)

bool firstrendersincereset = false;
std::vector<float>& buftemp;

std::vector<float> const firCoefficientsWindow;
};

Expand All @@ -118,6 +127,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) const override;
Expand All @@ -134,6 +147,7 @@ class Transverb final : public DfxPlugin {
}

std::array<uint32_t, dfx::TV::kNumDelays> speedModeStates {};
std::vector<float> buftemp; // shared between all DSP cores (memory optimization)
};


Expand Down
81 changes: 73 additions & 8 deletions transverb/transverbformalities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);

Expand Down Expand Up @@ -109,8 +112,11 @@ void Transverb::dfx_PostConstructor() {
TransverbDSP::TransverbDSP(DfxPlugin& inDfxPlugin)
: DfxPluginCore(inDfxPlugin),
MAXBUF(static_cast<int>(getparametermax_f(kBsize) * 0.001 * getsamplerate())),
buftemp(dynamic_cast<Transverb&>(inDfxPlugin).getscratchbuffer()),
firCoefficientsWindow(dfx::FIRFilter::generateKaiserWindow(kNumFIRTaps, 60.0f)) {

buftemp.assign(MAXBUF, 0.f);

registerSmoothedAudioValue(drymix);

for (auto& head : heads) {
Expand All @@ -126,12 +132,15 @@ TransverbDSP::TransverbDSP(DfxPlugin& inDfxPlugin)
void TransverbDSP::reset() {

std::ranges::for_each(heads, [](Head& head){ head.reset(); });

firstrendersincereset = true;
}

void TransverbDSP::Head::reset() {

smoothcount = 0;
lastdelayval = 0.f;
targetdist.reset();
filter.reset();
speedHasChanged = true;

Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -193,12 +200,12 @@ void TransverbDSP::processparameters() {
std::ranges::for_each(heads, [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<double>(bsize);
heads[head].read = fmod_bipolar(static_cast<double>(writer) - (*dist * bsize_f), bsize_f);
processdist(*dist, heads[head]);
}
}

Expand All @@ -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<double>(writer) - read, static_cast<double>(bsize));
}

void TransverbDSP::processdist(double distnormalized, Head& head) {

auto const bsize_f = static_cast<double>(bsize);
auto const distsamples = distnormalized * bsize_f;
auto const targetread = fmod_bipolar(static_cast<double>(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<size_t>(std::lround(copylength_f));
assert(copylength <= buftemp.size());
assert(static_cast<int>(copylength) <= bsize);
auto const sourcestart = subslice ? head.read : fmod_bipolar(static_cast<double>(writer) - (copylength_f * resamplerate), bsize_f);
for (size_t i = 0; i < copylength; i++)
{
auto const sourcepos = std::fmod(sourcestart + (resamplerate * static_cast<double>(i)), bsize_f);
buftemp[i] = interpolateHermite(head.buf.data(), sourcepos, bsize, writer);
}
auto const destinationstart = subslice ? std::lround(targetread) : mod_bipolar(writer - static_cast<int>(copylength), bsize);
auto const copylength1 = std::min(static_cast<size_t>(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;
}
}
}


Expand Down