From 930c089cbc29ff02ec2c44f4631f132942ba3e22 Mon Sep 17 00:00:00 2001 From: Tom 7 <499244+tom7@users.noreply.github.com> Date: Sat, 25 Jun 2022 11:05:25 -0400 Subject: [PATCH 1/3] Fix missing != operator for g++ 11.3 (perhaps it is defined for you via some generic template that converts operator==, but I don't think this is standard). Suppress some warnings; fix typo. --- dfx-library/dfxsettings.cpp | 6 +++--- dfxgui/dfxguislider.cpp | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/dfx-library/dfxsettings.cpp b/dfx-library/dfxsettings.cpp index cf3148a7..fada1015 100644 --- a/dfx-library/dfxsettings.cpp +++ b/dfx-library/dfxsettings.cpp @@ -21,7 +21,7 @@ along with Destroy FX Library. If not, see . 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 settings persistance mess. +Welcome to our settings persistence mess. ------------------------------------------------------------------------*/ #include "dfxsettings.h" @@ -434,7 +434,7 @@ if (!(oldVST && inIsPreset)) for (size_t i = 0; i < std::min(paramMap.size(), mParameterAssignments.size()); i++) { auto const mappedTag = paramMap[i]; - if ((mappedTag != dfx::kParameterID_Invalid) && (mappedTag >= 0) && (mappedTag < numStoredParameters)) + if ((mappedTag != dfx::kParameterID_Invalid) && (mappedTag >= 0) && ((unsigned)mappedTag < numStoredParameters)) { memcpy(&(mParameterAssignments[i]), newParameterAssignments + (mappedTag * storedParameterAssignmentSize), @@ -1403,7 +1403,7 @@ long DfxSettings::getParameterTagFromID(long inParamID, size_t inNumSearchIDs, i assert(inSearchIDs || (inNumSearchIDs == 0)); // search for the ID in the table that matches the requested ID - for (long i = 0; i < inNumSearchIDs; i++) + for (size_t i = 0; i < inNumSearchIDs; i++) { // return the parameter tag if a match is found if (inSearchIDs[i] == inParamID) diff --git a/dfxgui/dfxguislider.cpp b/dfxgui/dfxguislider.cpp index 7e9a562b..b8ce2268 100644 --- a/dfxgui/dfxguislider.cpp +++ b/dfxgui/dfxguislider.cpp @@ -88,6 +88,12 @@ static bool operator==(VSTGUI::Modifiers const& a, VSTGUI::Modifiers const& b) n return (std::memcmp(&a, &b, sizeof(a)) == 0); } +static bool operator!=(VSTGUI::Modifiers const& a, VSTGUI::Modifiers const& b) noexcept +{ + static_assert(dfx::IsTriviallySerializable); + return (std::memcmp(&a, &b, sizeof(a)) != 0); +} + //----------------------------------------------------------------------------- static void EndControl(IDGControl* inControl) { From c19bac206ec0351b7d2433df4465c52b579379a1 Mon Sep 17 00:00:00 2001 From: Sophia Poirier <2997196+sophiapoirier@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:26:44 -0700 Subject: [PATCH 2/3] update to and adopt some of C++20 (#65) --- bufferoverride/bufferoverride-base.h | 4 +- bufferoverride/bufferoverrideprocess.cpp | 11 +++-- bufferoverride/gui/bufferoverrideview.cpp | 16 +++---- bufferoverride/win32/makefile | 2 +- dfx-library/dfxenvelope.cpp | 2 +- dfx-library/dfxmath.h | 4 -- dfx-library/dfxmisc.cpp | 12 +----- dfx-library/dfxplugin.cpp | 8 ++-- dfx-library/dfxsettings.cpp | 29 +++++-------- dfx-library/dfxsettings.h | 15 +++---- dfx-library/firfilter.cpp | 52 ++++++++++++----------- dfx-library/firfilter.h | 23 +++++----- dfx-library/iirfilter.cpp | 32 +++++++++----- dfx-library/lfo.cpp | 8 ++-- dfx-library/xcode/dfxplugin.xcconfig | 2 +- dfxgui/dfxguieditor.cpp | 16 +++---- dfxgui/dfxguimisc.cpp | 8 ++-- eqsync/win32/makefile | 2 +- fonttest/win32/makefile | 2 +- geometer/geometer.cpp | 9 ++-- geometer/win32/makefile | 2 +- midigater/win32/makefile | 2 +- monomaker/win32/makefile | 2 +- polarizer/win32/makefile | 2 +- rezsynth/rezsynthformalities.cpp | 3 +- rezsynth/rezsynthsubprocesses.cpp | 15 ++----- rezsynth/win32/makefile | 2 +- scrubby/scrubbyprocess.cpp | 9 ++-- scrubby/win32/makefile | 2 +- skidder/win32/makefile | 2 +- thrush/thrush.cpp | 3 +- transverb/gui/transverbeditor.cpp | 10 ++--- transverb/transverb.h | 3 +- transverb/transverbprocess.cpp | 14 +++--- transverb/win32/makefile | 2 +- 35 files changed, 157 insertions(+), 173 deletions(-) diff --git a/bufferoverride/bufferoverride-base.h b/bufferoverride/bufferoverride-base.h index abf1d58d..63c83bed 100644 --- a/bufferoverride/bufferoverride-base.h +++ b/bufferoverride/bufferoverride-base.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ -Copyright (C) 2001-2021 Sophia Poirier and Tom Murphy VII +Copyright (C) 2001-2022 Sophia Poirier and Tom Murphy VII This file is part of Buffer Override. @@ -89,7 +89,7 @@ static_assert(dfx::IsTriviallySerializable); namespace detail { // where 16-byte lock-free atomics are not supported, we slice it into halves (Clang can but GCC cannot) -static constexpr auto getAtomicViewDataInstance() +static consteval auto getAtomicViewDataInstance() { using UnifiedT = std::atomic; diff --git a/bufferoverride/bufferoverrideprocess.cpp b/bufferoverride/bufferoverrideprocess.cpp index 9ea376bf..e75b819d 100644 --- a/bufferoverride/bufferoverrideprocess.cpp +++ b/bufferoverride/bufferoverrideprocess.cpp @@ -24,8 +24,7 @@ To contact the author, use the contact form at http://destroyfx.org/ #include #include - -#include "dfxmath.h" +#include @@ -233,10 +232,10 @@ void BufferOverride::updateBuffer(unsigned long samplePos, bool& ioViewDataChang // mSqrtFadeOut = std::sqrt(1.0f - mSmoothStep); // mSmoothFract = mSmoothStep; - mFadeOutGain = std::cos(dfx::math::kPi / static_cast(4 * mSmoothDur)); - mFadeInGain = std::sin(dfx::math::kPi / static_cast(4 * mSmoothDur)); - mRealFadePart = (mFadeOutGain * mFadeOutGain) - (mFadeInGain * mFadeInGain); // std::cos(dfx::math::kPi / 2.0f / n) - mImaginaryFadePart = 2.0f * mFadeOutGain * mFadeInGain; // std::sin(dfx::math::kPi / 2.0f / n) + mFadeOutGain = std::cos(std::numbers::pi_v / static_cast(4 * mSmoothDur)); + mFadeInGain = std::sin(std::numbers::pi_v / static_cast(4 * mSmoothDur)); + mRealFadePart = (mFadeOutGain * mFadeOutGain) - (mFadeInGain * mFadeInGain); // std::cos(std::numbers::pi_v / 2.f / n) + mImaginaryFadePart = 2.f * mFadeOutGain * mFadeInGain; // std::sin(std::numbers::pi_v / 2.f / n) } } diff --git a/bufferoverride/gui/bufferoverrideview.cpp b/bufferoverride/gui/bufferoverrideview.cpp index d6f7fc71..8d97a8bf 100644 --- a/bufferoverride/gui/bufferoverrideview.cpp +++ b/bufferoverride/gui/bufferoverrideview.cpp @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ -Copyright (C) 2021 Tom Murphy 7 and Sophia Poirier +Copyright (C) 2021-2022 Tom Murphy 7 and Sophia Poirier This file is part of Buffer Override. @@ -24,6 +24,7 @@ To contact the author, use the contact form at http://destroyfx.org/ #include #include #include +#include #include #include @@ -213,8 +214,8 @@ void BufferOverrideView::draw(VSTGUI::CDrawContext *ctx) { const auto legend_width = legend_right - legend_left; constexpr CCoord LEGEND_DASH_STRIDE = 2; constexpr CCoord LEGEND_DASH_WIDTH = 1; - // TODO: C++20 use std::midpoint - const auto legend_dash_y = std::floor((legend_top + legend_bottom) * 0.5); + const auto legend_dash_y = std::floor(std::midpoint(legend_top, + legend_bottom)); // legend bounding offc->setFrameColor(color_legend); @@ -231,12 +232,9 @@ void BufferOverrideView::draw(VSTGUI::CDrawContext *ctx) { } // legend label - const auto legend_label = [](auto window_sec) { - if (window_sec < 1) { - return std::to_string(std::lround(window_sec * 1000)) + " ms"; - } - return std::to_string(std::lround(window_sec)) + " sec"; - }(window_sec); + const auto legend_label = (window_sec < 1) ? + (std::to_string(std::lround(window_sec * 1000)) + " ms") : + (std::to_string(std::lround(window_sec)) + " sec"); constexpr bool LABEL_ANTIALIAS = false; constexpr CCoord LABEL_PADDING = 12; const auto label_width = offc->getStringWidth(legend_label.c_str()) + diff --git a/bufferoverride/win32/makefile b/bufferoverride/win32/makefile index 53c527a2..404c601f 100755 --- a/bufferoverride/win32/makefile +++ b/bufferoverride/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../bufferoverridedef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/dfx-library/dfxenvelope.cpp b/dfx-library/dfxenvelope.cpp index 568f9077..8f7f6386 100644 --- a/dfx-library/dfxenvelope.cpp +++ b/dfx-library/dfxenvelope.cpp @@ -262,7 +262,7 @@ double DfxEnvelope::calculateRise(double inPosNormalized) const } // sine fade (stupendously inefficient) -// return (std::sin((inPosNormalized * dfx::math::kPi) - (dfx::math::kPi * 0.5)) + 1.0) * 0.5; +// return (std::sin((inPosNormalized * std::numbers::pi_v) - (std::numbers::pi_v * 0.5)) + 1.) * 0.5; return inPosNormalized; } diff --git a/dfx-library/dfxmath.h b/dfx-library/dfxmath.h index 3ef95b7b..c2193fed 100644 --- a/dfx-library/dfxmath.h +++ b/dfx-library/dfxmath.h @@ -44,10 +44,6 @@ namespace dfx::math // constants //----------------------------------------------------------------------------- -// TODO: C++20 replace usage of this with std::numbers::pi_v -template >> -constexpr T kPi(3.14159265358979323846264338327950288); - // the AU SDK handles denormals for us, and ARM processors don't have denormal performance issues constexpr bool kDenormalProblem = #if defined(TARGET_API_AUDIOUNIT) || defined(__arm__) || defined(__arm64__) diff --git a/dfx-library/dfxmisc.cpp b/dfx-library/dfxmisc.cpp index 7e94d965..2a191eb3 100644 --- a/dfx-library/dfxmisc.cpp +++ b/dfx-library/dfxmisc.cpp @@ -274,16 +274,8 @@ bool LaunchDocumentation() // XXX this will load latest docs on our website which may not match the version of the running software // TODO: embed the documentation into Windows builds somehow? auto docsFileName = ToLower(PLUGIN_NAME_STRING ".html"); - while (true) - { - auto const isSpace = std::bind(std::isspace, std::placeholders::_1, std::locale::classic()); - auto const foundCharacter = std::find_if(docsFileName.cbegin(), docsFileName.cend(), isSpace); - if (foundCharacter == docsFileName.end()) - { - break; - } - docsFileName.erase(foundCharacter); - } + auto const isSpace = std::bind(std::isspace, std::placeholders::_1, std::locale::classic()); + std::erase_if(docsFileName, isSpace); return LaunchURL(DESTROYFX_URL "/docs/" + docsFileName); #endif // TARGET_OS_MAC diff --git a/dfx-library/dfxplugin.cpp b/dfx-library/dfxplugin.cpp index aa51d504..8e00c01a 100644 --- a/dfx-library/dfxplugin.cpp +++ b/dfx-library/dfxplugin.cpp @@ -1284,10 +1284,10 @@ void DfxPlugin::registerSmoothedAudioValue(dfx::ISmoothedValue* smoothedValue, D //----------------------------------------------------------------------------- void DfxPlugin::unregisterAllSmoothedAudioValues(DfxPluginCore* owner) { - mSmoothedAudioValues.erase(std::remove_if(mSmoothedAudioValues.begin(), mSmoothedAudioValues.end(), [owner](auto const& value) - { - return (owner == value.second); - }), mSmoothedAudioValues.cend()); + std::erase_if(mSmoothedAudioValues, [owner](auto const& value) + { + return (owner == value.second); + }); } //----------------------------------------------------------------------------- diff --git a/dfx-library/dfxsettings.cpp b/dfx-library/dfxsettings.cpp index fada1015..d3a9ba16 100644 --- a/dfx-library/dfxsettings.cpp +++ b/dfx-library/dfxsettings.cpp @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------ Destroy FX Library is a collection of foundation code for creating audio processing plug-ins. -Copyright (C) 2002-2021 Sophia Poirier +Copyright (C) 2002-2022 Sophia Poirier This file is part of the Destroy FX Library (version 1.0). @@ -321,7 +321,7 @@ try std::vector paramMap(mNumParameters, dfx::kParameterID_Invalid); for (size_t tag = 0; tag < mParameterIDs.size(); tag++) { - paramMap[tag] = getParameterTagFromID(mParameterIDs[tag], numStoredParameters, newParameterIDs); + paramMap[tag] = getParameterTagFromID(mParameterIDs[tag], {newParameterIDs, numStoredParameters}); } // point to the next data element after the parameter IDs: the first preset name @@ -434,10 +434,10 @@ if (!(oldVST && inIsPreset)) for (size_t i = 0; i < std::min(paramMap.size(), mParameterAssignments.size()); i++) { auto const mappedTag = paramMap[i]; - if ((mappedTag != dfx::kParameterID_Invalid) && (mappedTag >= 0) && ((unsigned)mappedTag < numStoredParameters)) + if ((mappedTag != dfx::kParameterID_Invalid) && (mappedTag >= 0) && (dfx::math::ToUnsigned(mappedTag) < numStoredParameters)) { memcpy(&(mParameterAssignments[i]), - newParameterAssignments + (mappedTag * storedParameterAssignmentSize), + newParameterAssignments + (dfx::math::ToUnsigned(mappedTag) * storedParameterAssignmentSize), copyParameterAssignmentSize); } } @@ -581,9 +581,8 @@ if (!(DFX_IsOldVstVersionNumber(storedVersion) && inIsPreset)) // and reverse the byte order of each event assignment auto const dataParameterAssignments = reinterpret_cast(dataPresets); validateRange(dataParameterAssignments, sizeof(*dataParameterAssignments) * numStoredParameters, "parameter assignments"); - for (uint32_t i = 0; i < numStoredParameters; i++) + for (auto& pa : std::span(dataParameterAssignments, numStoredParameters)) { - auto& pa = dataParameterAssignments[i]; dfx::ReverseBytes(pa.mEventType); dfx::ReverseBytes(pa.mEventChannel); dfx::ReverseBytes(pa.mEventNum); @@ -1398,21 +1397,13 @@ dfx::MidiEventType DfxSettings::getParameterAssignmentType(long inParamTag) cons //----------------------------------------------------------------------------- // given a parameter ID, find the tag (index) for that parameter in a table of parameter IDs -long DfxSettings::getParameterTagFromID(long inParamID, size_t inNumSearchIDs, int32_t const* inSearchIDs) +long DfxSettings::getParameterTagFromID(long inParamID, std::span inSearchIDs) { - assert(inSearchIDs || (inNumSearchIDs == 0)); - - // search for the ID in the table that matches the requested ID - for (size_t i = 0; i < inNumSearchIDs; i++) + auto const foundID = std::find(inSearchIDs.begin(), inSearchIDs.end(), inParamID); + if (foundID != inSearchIDs.end()) { - // return the parameter tag if a match is found - if (inSearchIDs[i] == inParamID) - { - return i; - } + return std::distance(inSearchIDs.begin(), foundID); } - - // if nothing was found, then return the error ID return dfx::kParameterID_Invalid; } @@ -1420,7 +1411,7 @@ long DfxSettings::getParameterTagFromID(long inParamID, size_t inNumSearchIDs, i // search using the internal table long DfxSettings::getParameterTagFromID(long inParamID) const { - return getParameterTagFromID(inParamID, mParameterIDs.size(), mParameterIDs.data()); + return getParameterTagFromID(inParamID, mParameterIDs); } diff --git a/dfx-library/dfxsettings.h b/dfx-library/dfxsettings.h index 219b6a87..28277a38 100644 --- a/dfx-library/dfxsettings.h +++ b/dfx-library/dfxsettings.h @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------ Destroy FX Library is a collection of foundation code for creating audio processing plug-ins. -Copyright (C) 2002-2021 Sophia Poirier +Copyright (C) 2002-2022 Sophia Poirier This file is part of the Destroy FX Library (version 1.0). @@ -73,7 +73,9 @@ This is our Destroy FX plugin data storage stuff #include +#include #include +#include #include #include "dfxdefines.h" @@ -307,13 +309,9 @@ class DfxSettings return mCrisisBehavior; } - static constexpr bool serializationIsNativeEndian() noexcept + static consteval bool serializationIsNativeEndian() noexcept { -#if __BIG_ENDIAN__ - return true; -#else - return false; -#endif + return std::endian::native == std::endian::big; } @@ -396,8 +394,7 @@ class DfxSettings return (inParamTag >= 0) && (static_cast(inParamTag) < mNumParameters); } - // TODO: C++20 use std::span - static long getParameterTagFromID(long inParamID, size_t inNumSearchIDs, int32_t const* inSearchIDs); + static long getParameterTagFromID(long inParamID, std::span inSearchIDs); long getParameterTagFromID(long inParamID) const; #if TARGET_PLUGIN_USES_MIDI diff --git a/dfx-library/firfilter.cpp b/dfx-library/firfilter.cpp index 28a7816b..4aa5bee2 100644 --- a/dfx-library/firfilter.cpp +++ b/dfx-library/firfilter.cpp @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------ Destroy FX Library is a collection of foundation code for creating audio processing plug-ins. -Copyright (C) 2002-2021 Sophia Poirier +Copyright (C) 2002-2022 Sophia Poirier This file is part of the Destroy FX Library (version 1.0). @@ -30,59 +30,59 @@ Welcome to our Finite Impulse Response filter. #include #include #include - -#include "dfxmath.h" +#include //----------------------------------------------------------------------------- float besselIZero(float input); -float besselIZero2(float input); +//float besselIZero2(float input); //----------------------------------------------------------------------------- // you're supposed to use use an odd number of taps void dfx::FIRFilter::calculateIdealLowpassCoefficients(double inCutoff, double inSampleRate, - size_t inNumTaps, float* outCoefficients) + std::span outCoefficients) { - assert(inNumTaps > 0); - assert(inNumTaps % 2); + assert(!outCoefficients.empty()); + assert(outCoefficients.size() % 2); // get the cutoff as a ratio of cutoff to Nyquist, scaled from 0 to Pi - double const corner = (inCutoff / (inSampleRate * 0.5)) * dfx::math::kPi; + double const corner = (inCutoff / (inSampleRate * 0.5)) * std::numbers::pi_v; size_t middleCoeff {}; - if (inNumTaps % 2) + if (outCoefficients.size() % 2) { - middleCoeff = (inNumTaps - 1) / 2; - outCoefficients[middleCoeff] = corner / dfx::math::kPi; + middleCoeff = (outCoefficients.size() - 1) / 2; + outCoefficients[middleCoeff] = corner / std::numbers::pi_v; } else { - middleCoeff = inNumTaps / 2; + middleCoeff = outCoefficients.size() / 2; } for (size_t n = 0; n < middleCoeff; n++) { - double const value = static_cast(n) - (static_cast(inNumTaps - 1) * 0.5); - outCoefficients[n] = std::sin(value * corner) / (value * dfx::math::kPi); - outCoefficients[inNumTaps - 1 - n] = outCoefficients[n]; + double const value = static_cast(n) - (static_cast(outCoefficients.size() - 1) * 0.5); + outCoefficients[n] = std::sin(value * corner) / (value * std::numbers::pi_v); + outCoefficients[outCoefficients.size() - 1 - n] = outCoefficients[n]; } } //----------------------------------------------------------------------------- void dfx::FIRFilter::calculateIdealLowpassCoefficients(double inCutoff, double inSampleRate, - size_t inNumTaps, float* outCoefficients, - float const* inCoefficientsWindow) + std::span outCoefficients, + std::span inCoefficientsWindow) { - calculateIdealLowpassCoefficients(inCutoff, inSampleRate, inNumTaps, outCoefficients); - std::transform(outCoefficients, outCoefficients + inNumTaps, inCoefficientsWindow, outCoefficients, std::multiplies()); + assert(outCoefficients.size() == inCoefficientsWindow.size()); + calculateIdealLowpassCoefficients(inCutoff, inSampleRate, outCoefficients); + std::transform(outCoefficients.begin(), outCoefficients.end(), inCoefficientsWindow.begin(), outCoefficients.begin(), std::multiplies<>{}); } //----------------------------------------------------------------------------- -void dfx::FIRFilter::applyKaiserWindow(size_t inNumTaps, float* ioCoefficients, float inAttenuation) +void dfx::FIRFilter::applyKaiserWindow(std::span ioCoefficients, float inAttenuation) { - assert(inNumTaps > 0); + assert(!ioCoefficients.empty()); // beta is 0 if the attenuation is less than 21 dB float beta = 0.0f; @@ -96,12 +96,12 @@ void dfx::FIRFilter::applyKaiserWindow(size_t inNumTaps, float* ioCoefficients, beta += 0.07886f * (inAttenuation - 21.0f); } - size_t const halfLength = (inNumTaps + 1) / 2; + size_t const halfLength = (ioCoefficients.size() + 1) / 2; auto const oneDivBesselIZeroOfBeta = 1.0f / besselIZero(beta); for (size_t n = 0; n < halfLength; n++) { - ioCoefficients[n] *= besselIZero(beta * std::sqrt(1.0f - std::pow((1.0f - ((2.0f * n) / static_cast(inNumTaps - 1))), 2.0f))) * oneDivBesselIZeroOfBeta; - ioCoefficients[inNumTaps - 1 - n] = ioCoefficients[n]; + ioCoefficients[n] *= besselIZero(beta * std::sqrt(1.0f - std::pow((1.0f - ((2.0f * n) / static_cast(ioCoefficients.size() - 1))), 2.0f))) * oneDivBesselIZeroOfBeta; + ioCoefficients[ioCoefficients.size() - 1 - n] = ioCoefficients[n]; } } @@ -109,7 +109,7 @@ void dfx::FIRFilter::applyKaiserWindow(size_t inNumTaps, float* ioCoefficients, std::vector dfx::FIRFilter::generateKaiserWindow(size_t inNumTaps, float inAttenuation) { std::vector coefficientsWindow(inNumTaps, 1.0f); - applyKaiserWindow(inNumTaps, coefficientsWindow.data(), inAttenuation); + applyKaiserWindow(coefficientsWindow, inAttenuation); return coefficientsWindow; } @@ -131,6 +131,7 @@ float besselIZero(float input) } //----------------------------------------------------------------------------- +#if 0 float besselIZero2(float input) { float sum = 1.0f; @@ -147,3 +148,4 @@ float besselIZero2(float input) return sum; } +#endif diff --git a/dfx-library/firfilter.h b/dfx-library/firfilter.h index df612218..cd121566 100644 --- a/dfx-library/firfilter.h +++ b/dfx-library/firfilter.h @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------ Destroy FX Library is a collection of foundation code for creating audio processing plug-ins. -Copyright (C) 2002-2021 Sophia Poirier +Copyright (C) 2002-2022 Sophia Poirier This file is part of the Destroy FX Library (version 1.0). @@ -28,6 +28,7 @@ Welcome to our Finite Impulse Response filter. #include +#include #include @@ -38,30 +39,28 @@ static constexpr double kShelfStartLowpass = 0.333; //----------------------------------------------------------------------------- -// TODO: C++20 use std::span for all pointer+size parameters void calculateIdealLowpassCoefficients(double inCutoff, double inSampleRate, - size_t inNumTaps, float* outCoefficients); + std::span outCoefficients); void calculateIdealLowpassCoefficients(double inCutoff, double inSampleRate, - size_t inNumTaps, float* outCoefficients, - float const* inCoefficientsWindow); -void applyKaiserWindow(size_t inNumTaps, float* ioCoefficients, float inAttenuation); + std::span outCoefficients, + std::span inCoefficientsWindow); +void applyKaiserWindow(std::span ioCoefficients, float inAttenuation); std::vector generateKaiserWindow(size_t inNumTaps, float inAttenuation); //----------------------------------------------------------------------------- -inline float process(float const* inAudio, size_t inNumTaps, float const* inCoefficients, - size_t inPos, size_t inBufferSize) +inline float process(std::span inAudio, std::span inCoefficients, size_t inPos) { float outval = 0.0f; - if ((inPos + inNumTaps) > inBufferSize) + if ((inPos + inCoefficients.size()) > inAudio.size()) { - for (size_t i = 0; i < inNumTaps; i++) + for (size_t i = 0; i < inCoefficients.size(); i++) { - outval += inAudio[(inPos + i) % inBufferSize] * inCoefficients[i]; + outval += inAudio[(inPos + i) % inAudio.size()] * inCoefficients[i]; } } else { - for (size_t i = 0; i < inNumTaps; i++) + for (size_t i = 0; i < inCoefficients.size(); i++) { outval += inAudio[inPos + i] * inCoefficients[i]; } diff --git a/dfx-library/iirfilter.cpp b/dfx-library/iirfilter.cpp index 6f2a8c06..f64a0766 100644 --- a/dfx-library/iirfilter.cpp +++ b/dfx-library/iirfilter.cpp @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------ Destroy FX Library is a collection of foundation code for creating audio processing plug-ins. -Copyright (C) 2001-2021 Sophia Poirier +Copyright (C) 2001-2022 Sophia Poirier This file is part of the Destroy FX Library (version 1.0). @@ -28,13 +28,15 @@ Welcome to our Infinite Impulse Response filter. #include #include +#include +#include #include #include "dfxmath.h" //------------------------------------------------------------------------ -static double const kDefaultQ_LP_HP = std::sqrt(2.0) / 2.0; // C++20 constexpr 1.0 / std::numbers::sqrt2_v +constexpr double kDefaultQ_LP_HP = std::numbers::sqrt2_v / 2.; constexpr double kUnityGain = 1.; //------------------------------------------------------------------------ @@ -48,11 +50,11 @@ struct PreCoeff double mBeta {}; // shelf PreCoeff(double inFrequency, double inQ, double inSampleRate) - : mOmega(2. * dfx::math::kPi * inFrequency / inSampleRate), + : mOmega(2. * std::numbers::pi_v * inFrequency / inSampleRate), mSinOmega(std::sin(mOmega)), mCosOmega(std::cos(mOmega)), mAlpha(mSinOmega / (2. * inQ)) - //mAlpha(mSinOmega * std::sinh(std::numbers::ln2 / 2. * inQ * mOmega / mSinOmega)) // http://musicdsp.org/showone.php?id=64 + //mAlpha(mSinOmega * std::sinh(std::numbers::ln2_v / 2. * inQ * mOmega / mSinOmega)) // http://musicdsp.org/showone.php?id=64 { } @@ -179,7 +181,13 @@ void dfx::IIRFilter::setCoefficients(Coefficients const& inCoefficients) dfx::IIRFilter::Coefficients const& dfx::IIRFilter::setCoefficients(FilterType inFilterType, double inFrequency, double inQ, double inGain) { assert(inFrequency > 0.); + assert(!std::isinf(inFrequency)); + assert(!std::isnan(inFrequency)); assert(inQ > 0.); + assert(!std::isinf(inQ)); + assert(!std::isnan(inQ)); + assert(!std::isinf(inGain)); + assert(!std::isnan(inGain)); mFilterType = inFilterType; mFilterFrequency = inFrequency; @@ -197,7 +205,11 @@ dfx::IIRFilter::Coefficients const& dfx::IIRFilter::setCoefficients(FilterType i (inFilterType != FilterType::LowShelf) && (inFilterType != FilterType::HighShelf)); assert(inFrequency > 0.); + assert(!std::isinf(inFrequency)); + assert(!std::isnan(inFrequency)); assert(inQ > 0.); + assert(!std::isinf(inQ)); + assert(!std::isnan(inQ)); mFilterType = inFilterType; mFilterFrequency = inFrequency; @@ -225,6 +237,7 @@ dfx::IIRFilter::Coefficients const& dfx::IIRFilter::setLowpassCoefficients(doubl //------------------------------------------------------------------------ dfx::IIRFilter::Coefficients const& dfx::IIRFilter::setHighpassCoefficients(double inCutoffFrequency) { + assert(inCutoffFrequency <= mSampleRate); return setCoefficients(FilterType::Highpass, inCutoffFrequency, kDefaultQ_LP_HP); } @@ -261,7 +274,7 @@ dfx::Crossover::Crossover(unsigned long inChannelCount, double inSampleRate, dou { assert(inSampleRate > 0.); assert(inFrequency > 0.); - assert(inFrequency <= (inSampleRate / 2.)); + //assert(inFrequency <= (inSampleRate / 2.)); inFrequency = std::min(inFrequency, inSampleRate / 2.); // upper-limit to Nyquist #if !DFX_CROSSOVER_LINKWITZ_RILEY_MUSICDSP @@ -287,17 +300,16 @@ void dfx::Crossover::setFrequency(double inFrequency) { #if DFX_CROSSOVER_LINKWITZ_RILEY_MUSICDSP // https://www.musicdsp.org/en/latest/Filters/266-4th-order-linkwitz-riley-filters.html - double const wc = 2. * dfx::math::kPi * inFrequency; + double const wc = 2. * std::numbers::pi_v * inFrequency; auto const wc2 = wc * wc; auto const wc3 = wc2 * wc; auto const wc4 = wc2 * wc2; - double const k = wc / std::tan(dfx::math::kPi * inFrequency / mSampleRate); + double const k = wc / std::tan(std::numbers::pi_v * inFrequency / mSampleRate); auto const k2 = k * k; auto const k3 = k2 * k; auto const k4 = k2 * k2; - static double const sqrt2 = std::sqrt(2.); - auto const sq_tmp1 = sqrt2 * wc3 * k; - auto const sq_tmp2 = sqrt2 * wc * k3; + auto const sq_tmp1 = std::numbers::sqrt2_v * wc3 * k; + auto const sq_tmp2 = std::numbers::sqrt2_v * wc * k3; auto const a_tmp = (4. * wc2 * k2) + (2. * sq_tmp1) + k4 + (2. * sq_tmp2) + wc4; auto const a_tmp_inv = (a_tmp != 0.) ? (1. / a_tmp) : 1.; diff --git a/dfx-library/lfo.cpp b/dfx-library/lfo.cpp index 55c46ca0..119ca8c7 100644 --- a/dfx-library/lfo.cpp +++ b/dfx-library/lfo.cpp @@ -29,8 +29,8 @@ Welcome to our Low Frequency Oscillator. #include #include #include - -#include "dfxmath.h" +#include +#include //------------------------------------------------------------------------ @@ -191,7 +191,7 @@ double dfx::LFO::process() const if (mShape == kShape_RandomInterpolating) { // interpolate between the previous random number and the new one - outValue = (mRandomNumber * mPosition) + (mPrevRandomNumber * (1. - mPosition)); + outValue = std::lerp(mPrevRandomNumber, mRandomNumber, mPosition); } else if (mShape == kShape_Random) { @@ -209,7 +209,7 @@ double dfx::LFO::process() const // oscillates from 0 to 1 and back to 0 double dfx::LFO::sineGenerator(double inPosition) { - return (std::sin((inPosition - 0.25) * 2. * dfx::math::kPi) + 1.) * 0.5; + return (std::sin((inPosition - 0.25) * 2. * std::numbers::pi_v) + 1.) * 0.5; } //----------------------------------------------------------------------------------------- diff --git a/dfx-library/xcode/dfxplugin.xcconfig b/dfx-library/xcode/dfxplugin.xcconfig index 19cbaab3..3efd30ef 100644 --- a/dfx-library/xcode/dfxplugin.xcconfig +++ b/dfx-library/xcode/dfxplugin.xcconfig @@ -7,7 +7,7 @@ DEPLOYMENT_LOCATION = YES DSTROOT = / SKIP_INSTALL = NO GCC_C_LANGUAGE_STANDARD = c99 -CLANG_CXX_LANGUAGE_STANDARD = c++17 +CLANG_CXX_LANGUAGE_STANDARD = c++20 CLANG_CXX_LIBRARY = libc++ CLANG_ENABLE_OBJC_ARC = YES ENABLE_STRICT_OBJC_MSGSEND = YES diff --git a/dfxgui/dfxguieditor.cpp b/dfxgui/dfxguieditor.cpp index 72ea085c..bf447ae9 100644 --- a/dfxgui/dfxguieditor.cpp +++ b/dfxgui/dfxguieditor.cpp @@ -220,10 +220,10 @@ bool DfxGuiEditor::open(void* inWindow) #else mParameterList.assign(static_cast(GetNumParameters()), dfx::kParameterID_Invalid); std::iota(mParameterList.begin(), mParameterList.end(), 0); - mParameterList.erase(std::remove_if(mParameterList.begin(), mParameterList.end(), [this](auto parameterID) - { - return HasParameterAttribute(parameterID, DfxParam::kAttribute_Unused); - }), mParameterList.cend()); + std::erase_if(mParameterList, [this](auto parameterID) + { + return HasParameterAttribute(parameterID, DfxParam::kAttribute_Unused); + }); #endif mEditorOpenErr = OpenEditor(); @@ -576,10 +576,10 @@ void DfxGuiEditor::randomizeparameters(bool inWriteAutomation) { auto parameterList = GetParameterList(); // TODO: C++20 use ranges view filter - parameterList.erase(std::remove_if(parameterList.begin(), parameterList.end(), [this](auto parameterID) - { - return HasParameterAttribute(parameterID, DfxParam::kAttribute_OmitFromRandomizeAll); - }), parameterList.cend()); + std::erase_if(parameterList, [this](auto parameterID) + { + return HasParameterAttribute(parameterID, DfxParam::kAttribute_OmitFromRandomizeAll); + }); if (inWriteAutomation) { diff --git a/dfxgui/dfxguimisc.cpp b/dfxgui/dfxguimisc.cpp index 72560b44..27aedfda 100644 --- a/dfxgui/dfxguimisc.cpp +++ b/dfxgui/dfxguimisc.cpp @@ -26,6 +26,7 @@ To contact the author, use the contact form at http://destroyfx.org/ #include #include #include +#include #include #include "dfxmisc.h" @@ -128,7 +129,7 @@ DGColor DGColor::getSystem(System inSystemColorID) }; #define DFX_SELECTOR(x) @selector(x) #else - auto const fromNSColorProperty = []() + auto const fromNSColorProperty = [] { return OptionalColor(); }; @@ -197,8 +198,9 @@ std::string dfx::SanitizeNumericalInput(std::string const& inText) { // remove digit separators // XXX TODO: this doesn't support locale, assumes comma - std::string resultText(inText.size(), '\0'); - resultText.erase(std::remove_copy(inText.cbegin(), inText.cend(), resultText.begin(), ','), resultText.cend()); + auto resultText = inText; + // TODO: C++20 bind_front + std::erase_if(resultText, std::bind(std::equal_to<>{}, std::placeholders::_1, ',')); // trim white space and any other noise (with respect to numerical parsing) while (!resultText.empty()) diff --git a/eqsync/win32/makefile b/eqsync/win32/makefile index 1370cc5e..720f583f 100755 --- a/eqsync/win32/makefile +++ b/eqsync/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../eqsyncdef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/fonttest/win32/makefile b/fonttest/win32/makefile index d2aea0ea..d7965e5b 100755 --- a/fonttest/win32/makefile +++ b/fonttest/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../fonttestdef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/geometer/geometer.cpp b/geometer/geometer.cpp index 0e448244..bb6dd217 100644 --- a/geometer/geometer.cpp +++ b/geometer/geometer.cpp @@ -32,6 +32,7 @@ Featuring the Super Destroy FX Windowing System! #include #include #include +#include #include #include @@ -272,7 +273,7 @@ void PLUGIN::updatewindowcache(PLUGINCORE * geometercore) std::copy_n(geometercore->getinput(), GeometerViewData::samples, windowcache_writer->inputs.data()); #else for (int i=0; i < GeometerViewData::samples; i++) { - windowcache_writer->inputs[i] = std::sin((i * 10 * dfx::math::kPi) / GeometerViewData::samples); + windowcache_writer->inputs[i] = std::sin((i * 10 * std::numbers::pi_v) / GeometerViewData::samples); } #endif @@ -1049,7 +1050,7 @@ int PLUGINCORE::processw(float const * in, float * out, int samples, for(int z=px[u-1]; z < px[u]; z++) { float const pct = (float)(z-px[u-1]) / denom; - float p = 0.5f * (-std::cos(dfx::math::kPi * pct) + 1.0f); + float p = 0.5f * (-std::cos(std::numbers::pi_v * pct) + 1.0f); if (interparam > 0.5f) { p = std::pow(p, (interparam - 0.16666667f) * 3.0f); @@ -1121,7 +1122,7 @@ int PLUGINCORE::processw(float const * in, float * out, int samples, for(int z=px[u-1]; z < px[u]; z++) { float const pct = (float)(z-px[u-1]) * oodenom; - float const wand = sinf(2.0f * dfx::math::kPi * pct); + float const wand = sinf(2.0f * std::numbers::pi_v * pct); out[z] = wand * interparam + ((1.0f-interparam) * @@ -1290,7 +1291,7 @@ void PLUGINCORE::updatewindowshape() break; case WINDOW_COS: for(int z = 0; z < third; z++) { - float const p = 0.5f * (-std::cos(dfx::math::kPi * (static_cast(z) * oneDivThird)) + 1.0f); + float const p = 0.5f * (-std::cos(std::numbers::pi_v * (static_cast(z) * oneDivThird)) + 1.0f); windowenvelope[z] = p; windowenvelope[z+third] = (1.0f - p); } diff --git a/geometer/win32/makefile b/geometer/win32/makefile index aae05836..84ea584f 100755 --- a/geometer/win32/makefile +++ b/geometer/win32/makefile @@ -19,7 +19,7 @@ VSTGUI=../../vstgui/vstgui # https://sourceforge.net/p/mingw-w64/mailman/mingw-w64-public/thread/20200920130036.1e6b6508%40ext.localdomain/ DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../geometerdef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/midigater/win32/makefile b/midigater/win32/makefile index 6a6b0549..9235f51c 100644 --- a/midigater/win32/makefile +++ b/midigater/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../midigaterdef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/monomaker/win32/makefile b/monomaker/win32/makefile index 2378c49d..afb69647 100755 --- a/monomaker/win32/makefile +++ b/monomaker/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../monomakerdef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/polarizer/win32/makefile b/polarizer/win32/makefile index 4b878aab..ca7e3728 100755 --- a/polarizer/win32/makefile +++ b/polarizer/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../polarizerdef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/rezsynth/rezsynthformalities.cpp b/rezsynth/rezsynthformalities.cpp index 0e985392..fcedba28 100644 --- a/rezsynth/rezsynthformalities.cpp +++ b/rezsynth/rezsynthformalities.cpp @@ -22,6 +22,7 @@ To contact the author, use the contact form at http://destroyfx.org/ #include "rezsynth.h" #include +#include #include "dfxmath.h" #include "dfxmisc.h" @@ -122,7 +123,7 @@ RezSynth::RezSynth(TARGET_API_BASE_INSTANCE_TYPE inInstance) long RezSynth::initialize() { // these are values that are always needed during calculateCoefficients - mPiDivSR = dfx::math::kPi / getsamplerate(); + mPiDivSR = std::numbers::pi_v / getsamplerate(); mTwoPiDivSR = mPiDivSR * 2.0; mNyquist = getsamplerate() / 2.0; diff --git a/rezsynth/rezsynthsubprocesses.cpp b/rezsynth/rezsynthsubprocesses.cpp index 642882b2..f32e1f51 100644 --- a/rezsynth/rezsynthsubprocesses.cpp +++ b/rezsynth/rezsynthsubprocesses.cpp @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ -Copyright (C) 2001-2021 Sophia Poirier +Copyright (C) 2001-2022 Sophia Poirier This file is part of Rez Synth. @@ -40,7 +40,6 @@ double RezSynth::calculateAmpEvener(int currentNote) const if (mScaleMode <= kScaleMode_None) { -// ampEvener = 0.0000000000009 * std::pow((static_cast(fBandwidth) + 0.72), 1.8) * baseFreq * baseFreq / std::sqrt(static_cast(mNumBands) * 0.0000003); ampEvener = 0.0000000000009 * std::pow(contractparametervalue(kBandwidthAmount_Hz, noteBandwidth) + 0.72, 1.8) * baseFreq * baseFreq / std::sqrt(static_cast(mNumBands) * 0.0000003); } else if (mScaleMode == kScaleMode_RMS) @@ -210,11 +209,7 @@ void RezSynth::processFilterOuts(float const* const* inAudio, float* const* outA { auto const clampInfinities = [](double value) { -#if __GNUC__ - if (__builtin_expect(std::isinf(value), 0)) // TODO: C++20 [[unlikely]] -#else - if (std::isinf(value)) -#endif + if (std::isinf(value)) [[unlikely]] { return std::copysign(std::numeric_limits::max(), value); } @@ -248,11 +243,7 @@ void RezSynth::processFilterOuts(float const* const* inAudio, float* const* outA outAudio[ch][sampleIndex] += scaledBandOutputSum * envedTotalAmp; } -#if __GNUC__ - if (__builtin_expect(std::isinf(outAudio[ch][sampleIndex]), 0)) // TODO: C++20 [[unlikely]] -#else - if (std::isinf(outAudio[ch][sampleIndex])) -#endif + if (std::isinf(outAudio[ch][sampleIndex])) [[unlikely]] { outAudio[ch][sampleIndex] = entryOutput; } diff --git a/rezsynth/win32/makefile b/rezsynth/win32/makefile index 173e8672..17311e55 100755 --- a/rezsynth/win32/makefile +++ b/rezsynth/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../rezsynthdef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/scrubby/scrubbyprocess.cpp b/scrubby/scrubbyprocess.cpp index fff03e1d..f4dfd607 100644 --- a/scrubby/scrubbyprocess.cpp +++ b/scrubby/scrubbyprocess.cpp @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ -Copyright (C) 2002-2021 Sophia Poirier +Copyright (C) 2002-2022 Sophia Poirier This file is part of Scrubby. @@ -23,6 +23,7 @@ To contact the author, use the contact form at http://destroyfx.org/ #include #include +#include #include "dfxmath.h" @@ -37,7 +38,7 @@ inline double calculateTargetSpeed(double a, double n, double k) double const lambertInput = (n * a) / k; double b = k * dfx::math::LambertW(lambertInput) / n; // the target speed // cuz I don't totally trust my Lambert W function... - if (std::isnan(b) || !std::isfinite(b)) + if (!std::isfinite(b)) { b = 1.0; } @@ -430,8 +431,8 @@ void Scrubby::processaudio(float const* const* inAudio, float* const* outAudio, #if 0 // melody test for (unsigned long ch = 0; ch < numChannels; ch++) { - mAudioBuffers[ch][mWritePos] = 0.69f * std::sin(24.0f * dfx::math::kPi * (static_cast(samplecount) / static_cast(inNumFrames))); - mAudioBuffers[ch][mWritePos] = 0.69f * std::sin(2.0f * dfx::math::kPi * (static_cast(mSineCount) / 169.0f)); + mAudioBuffers[ch][mWritePos] = 0.69f * std::sin(24.f * std::numbers::pi_v * (static_cast(samplecount) / static_cast(inNumFrames))); + mAudioBuffers[ch][mWritePos] = 0.69f * std::sin(2.f * std::numbers::pi_v * (static_cast(mSineCount) / 169.f)); } // produce a sine wave of C4 when using 44.1 kHz sample rate if (++mSineCount > 168) diff --git a/scrubby/win32/makefile b/scrubby/win32/makefile index 2a345b46..93788876 100755 --- a/scrubby/win32/makefile +++ b/scrubby/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../scrubbydef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/skidder/win32/makefile b/skidder/win32/makefile index c5f686b7..155ad462 100755 --- a/skidder/win32/makefile +++ b/skidder/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../skidderdef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. diff --git a/thrush/thrush.cpp b/thrush/thrush.cpp index 913b8231..528a375a 100644 --- a/thrush/thrush.cpp +++ b/thrush/thrush.cpp @@ -23,6 +23,7 @@ To contact the author, use the contact form at http://destroyfx.org/ #include #include +#include #include "dfxmisc.h" @@ -223,7 +224,7 @@ double Thrush::processLFOs(ThrushLFO& lfoLayer1, ThrushLFO& lfoLayer2) const auto lfoOffset = lfoLayer2.process(); // this is the offset from the first layer LFO's rate, caused by the second layer LFO // scale the 0 - 1 LFO output value to the depth range of the second layer LFO - lfoOffset = (lfoOffset * (kLFO2DepthMax - kLFO2DepthMin)) + kLFO2DepthMin; + lfoOffset = std::lerp(kLFO2DepthMin, kLFO2DepthMax, lfoOffset); // update the first layer LFO's cycle phase step size as modulated by the second layer LFO lfoLayer1.setStepSize(lfoLayer1.mEffectiveRateHz * lfoOffset * mOneDivSR); diff --git a/transverb/gui/transverbeditor.cpp b/transverb/gui/transverbeditor.cpp index 8a1e8133..3237e650 100644 --- a/transverb/gui/transverbeditor.cpp +++ b/transverb/gui/transverbeditor.cpp @@ -153,12 +153,12 @@ static bool speedDisplayProcedure(float inValue, char* outText, void*) static std::optional speedTextConvertProcedure(std::string const& inText, DGTextDisplay*) { - std::string filteredText(inText.size(), '\0'); + auto filteredText = inText; // TODO: does not support locale for number format, and ignores minus and periods that are not part of fractional numbers - filteredText.erase(std::remove_copy_if(inText.cbegin(), inText.cend(), filteredText.begin(), [](auto character) - { - return !(std::isdigit(character) || std::isspace(character) || (character == '-') || (character == '.')); - }), filteredText.cend()); + std::erase_if(filteredText, [](auto character) + { + return !(std::isdigit(character) || std::isspace(character) || (character == '-') || (character == '.')); + }); float octaves = 0.0f, semitones = 0.0f; auto const scanCount = sscanf(filteredText.c_str(), "%f%f", &octaves, &semitones); diff --git a/transverb/transverb.h b/transverb/transverb.h index 7d8724a5..2ce28536 100644 --- a/transverb/transverb.h +++ b/transverb/transverb.h @@ -25,6 +25,7 @@ To contact the author, use the contact form at http://destroyfx.org/ #include #include #include +#include #include #include "dfxplugin.h" @@ -74,7 +75,7 @@ class TransverbDSP final : public DfxPluginCore { static constexpr float interpolateLinear(float value1, float value2, double address) { auto const posFract = static_cast(std::fmod(address, 1.)); - return (value1 * (1.0f - posFract)) + (value2 * posFract); + return std::lerp(value1, value2, posFract); } static constexpr float interpolateLinear(float const* data, double readaddress, int arraysize/*, int writeaddress*/); diff --git a/transverb/transverbprocess.cpp b/transverb/transverbprocess.cpp index 65fd9316..803ed4dd 100644 --- a/transverb/transverbprocess.cpp +++ b/transverb/transverbprocess.cpp @@ -24,6 +24,7 @@ To contact the author, use the contact form at http://destroyfx.org/ #include #include #include +#include #include "dfxmath.h" #include "firfilter.h" @@ -90,8 +91,7 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, unsigned long if (std::exchange(heads[h].speedHasChanged, false)) { dfx::FIRFilter::calculateIdealLowpassCoefficients((samplerate / heads[h].speed.getValue()) * dfx::FIRFilter::kShelfStartLowpass, - samplerate, kNumFIRTaps, heads[h].firCoefficients.data(), - firCoefficientsWindow.data()); + samplerate, heads[h].firCoefficients, firCoefficientsWindow); heads[h].filter.reset(); } } @@ -138,10 +138,10 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, unsigned long case FilterMode::LowpassFIR: { // get two consecutive FIR output values for linear interpolation - auto const lp1 = dfx::FIRFilter::process(heads[h].buf.data(), kNumFIRTaps, heads[h].firCoefficients.data(), - mod_bipolar(read_int - static_cast(kNumFIRTaps), bsize), bsize); - auto const lp2 = dfx::FIRFilter::process(heads[h].buf.data(), kNumFIRTaps, heads[h].firCoefficients.data(), - mod_bipolar(read_int - static_cast(kNumFIRTaps) + 1, bsize), bsize); + auto const lp1 = dfx::FIRFilter::process(std::span(heads[h].buf).subspan(0, bsize), heads[h].firCoefficients, + mod_bipolar(read_int - static_cast(kNumFIRTaps), bsize)); + auto const lp2 = dfx::FIRFilter::process(std::span(heads[h].buf).subspan(0, bsize), heads[h].firCoefficients, + mod_bipolar(read_int - static_cast(kNumFIRTaps) + 1, bsize)); // interpolate output linearly (avoid shit sound) and compensate gain delayvals[h] = interpolateLinear(lp1, lp2, heads[h].read) * mugs[h]; break; @@ -157,7 +157,7 @@ void TransverbDSP::process(float const* inAudio, float* outAudio, unsigned long // the current sample if smoothing is in progress if (heads[h].smoothcount > 0) { auto const smoothpos = heads[h].smoothstep * static_cast(heads[h].smoothcount); - delayvals[h] = (delayvals[h] * (1.f - smoothpos)) + (heads[h].lastdelayval * smoothpos); + delayvals[h] = std::lerp(delayvals[h], heads[h].lastdelayval, smoothpos); heads[h].smoothcount--; } diff --git a/transverb/win32/makefile b/transverb/win32/makefile index 5203e074..2c267450 100755 --- a/transverb/win32/makefile +++ b/transverb/win32/makefile @@ -17,7 +17,7 @@ VSTGUI=../../vstgui/vstgui # -DENABLE_TRACE=1 DEFINES=-DWIN32=1 -D_WIN32_WINNT=0x0601 -DTARGET_OS_WIN32=1 -DTARGET_API_VST=1 -DVSTGUI_ENABLE_DEPRECATED_METHODS=0 -DNDEBUG=1 -DGetMatchingFonts=GetMatchingFonts_ INCLUDES=-I .. -I $(DFXLIB) -I $(VSTSDK) -I $(DFXGUI) -I $(VSTGUI) -I $(VSTGUI)/plugin-bindings -include "../transverbdef.h" -CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++17 -O2 +CXXFLAGS=$(DEFINES) $(INCLUDES) -m64 -Wall -Wno-unknown-pragmas --std=c++20 -O2 # -mwindows to select the GUI subsystem (not console) # -s (strip unexported symbols) for release! 18mb DLL unstripped, 2.5mb stripped. # -static-libgcc and -static-libstdc++ avoid having the output depend on these mingw DLLs. From 69561071a6d56b5867fcbff256b9bc5be41ea9a2 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 3/3] 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 969ea82a..27a64631 100644 --- a/transverb/transverb-base.h +++ b/transverb/transverb-base.h @@ -47,6 +47,7 @@ enum kTomsound, kFreeze, kAttenuateFeedbackByMixLevel, + kDistChangeMode, kNumParameters }; @@ -65,6 +66,8 @@ enum { kQualityMode_DirtFi, kQualityMode_HiFi, kQualityMode_UltraHiFi, kQualityM enum { 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 2ce28536..61804eb1 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 { long dfx_SetProperty(dfx::PropertyID inPropertyID, dfx::Scope inScope, unsigned long 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 e87b1079..93947a8b 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); setparameterenforcevaluelimits(kDist1, true); @@ -66,10 +67,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); @@ -108,8 +111,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) { @@ -125,12 +131,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; @@ -163,8 +172,6 @@ void TransverbDSP::processparameters() { heads[head].feed = *value; } } - quality = getparameter_i(kQuality); - tomsound = getparameter_b(kTomsound); if (auto const value = getparameterifchanged_f(kBsize)) { @@ -192,12 +199,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]); } } @@ -220,6 +227,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 803ed4dd..5927940c 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, unsigned long 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, unsigned long 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, unsigned long { 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, unsigned long 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, unsigned long // 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, unsigned long } // 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, unsigned long 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(), lowpasspos[h], 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(), lowpasspos[h], 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(), lowpasspos[h], 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;