From a809c03f87afc6d17b5b2e180c6f6d993247418c Mon Sep 17 00:00:00 2001 From: Lost Robot <34612565+LostRobotMusic@users.noreply.github.com> Date: Mon, 13 Oct 2025 14:18:34 -0500 Subject: [PATCH] Add SlewDistortion effect and oversampling support (#7641) --- cmake/modules/PluginList.cmake | 1 + include/Draggable.h | 76 ++ include/FloatModelEditorBase.h | 12 +- include/OversamplingHelpers.h | 249 ++++++ include/lmms_math.h | 75 ++ plugins/SlewDistortion/CMakeLists.txt | 4 + plugins/SlewDistortion/SlewDistortion.cpp | 720 ++++++++++++++++++ plugins/SlewDistortion/SlewDistortion.h | 91 +++ .../SlewDistortionControlDialog.cpp | 494 ++++++++++++ .../SlewDistortionControlDialog.h | 84 ++ .../SlewDistortion/SlewDistortionControls.cpp | 184 +++++ .../SlewDistortion/SlewDistortionControls.h | 120 +++ plugins/SlewDistortion/artwork.png | Bin 0 -> 128479 bytes plugins/SlewDistortion/dc_off.png | Bin 0 -> 1334 bytes plugins/SlewDistortion/dc_on.png | Bin 0 -> 2064 bytes plugins/SlewDistortion/handle.png | Bin 0 -> 810 bytes plugins/SlewDistortion/handle_zero.png | Bin 0 -> 4868 bytes plugins/SlewDistortion/help_off.png | Bin 0 -> 667 bytes plugins/SlewDistortion/help_on.png | Bin 0 -> 595 bytes plugins/SlewDistortion/link_off.png | Bin 0 -> 623 bytes plugins/SlewDistortion/link_on.png | Bin 0 -> 945 bytes plugins/SlewDistortion/logo.png | Bin 0 -> 774 bytes plugins/SlewDistortion/mb_off.png | Bin 0 -> 1215 bytes plugins/SlewDistortion/mb_on.png | Bin 0 -> 1801 bytes plugins/SlewDistortion/oversample_16x_off.png | Bin 0 -> 793 bytes plugins/SlewDistortion/oversample_16x_on.png | Bin 0 -> 910 bytes plugins/SlewDistortion/oversample_1x_off.png | Bin 0 -> 677 bytes plugins/SlewDistortion/oversample_1x_on.png | Bin 0 -> 716 bytes plugins/SlewDistortion/oversample_2x_off.png | Bin 0 -> 694 bytes plugins/SlewDistortion/oversample_2x_on.png | Bin 0 -> 802 bytes plugins/SlewDistortion/oversample_32x_off.png | Bin 0 -> 849 bytes plugins/SlewDistortion/oversample_32x_on.png | Bin 0 -> 983 bytes plugins/SlewDistortion/oversample_4x_off.png | Bin 0 -> 666 bytes plugins/SlewDistortion/oversample_4x_on.png | Bin 0 -> 768 bytes plugins/SlewDistortion/oversample_8x_off.png | Bin 0 -> 751 bytes plugins/SlewDistortion/oversample_8x_on.png | Bin 0 -> 804 bytes src/gui/CMakeLists.txt | 1 + src/gui/widgets/Draggable.cpp | 128 ++++ 38 files changed, 2232 insertions(+), 7 deletions(-) create mode 100644 include/Draggable.h create mode 100755 include/OversamplingHelpers.h create mode 100755 plugins/SlewDistortion/CMakeLists.txt create mode 100755 plugins/SlewDistortion/SlewDistortion.cpp create mode 100755 plugins/SlewDistortion/SlewDistortion.h create mode 100755 plugins/SlewDistortion/SlewDistortionControlDialog.cpp create mode 100755 plugins/SlewDistortion/SlewDistortionControlDialog.h create mode 100755 plugins/SlewDistortion/SlewDistortionControls.cpp create mode 100755 plugins/SlewDistortion/SlewDistortionControls.h create mode 100644 plugins/SlewDistortion/artwork.png create mode 100644 plugins/SlewDistortion/dc_off.png create mode 100644 plugins/SlewDistortion/dc_on.png create mode 100644 plugins/SlewDistortion/handle.png create mode 100644 plugins/SlewDistortion/handle_zero.png create mode 100644 plugins/SlewDistortion/help_off.png create mode 100644 plugins/SlewDistortion/help_on.png create mode 100644 plugins/SlewDistortion/link_off.png create mode 100644 plugins/SlewDistortion/link_on.png create mode 100755 plugins/SlewDistortion/logo.png create mode 100644 plugins/SlewDistortion/mb_off.png create mode 100644 plugins/SlewDistortion/mb_on.png create mode 100644 plugins/SlewDistortion/oversample_16x_off.png create mode 100644 plugins/SlewDistortion/oversample_16x_on.png create mode 100644 plugins/SlewDistortion/oversample_1x_off.png create mode 100644 plugins/SlewDistortion/oversample_1x_on.png create mode 100644 plugins/SlewDistortion/oversample_2x_off.png create mode 100644 plugins/SlewDistortion/oversample_2x_on.png create mode 100644 plugins/SlewDistortion/oversample_32x_off.png create mode 100644 plugins/SlewDistortion/oversample_32x_on.png create mode 100644 plugins/SlewDistortion/oversample_4x_off.png create mode 100644 plugins/SlewDistortion/oversample_4x_on.png create mode 100644 plugins/SlewDistortion/oversample_8x_off.png create mode 100644 plugins/SlewDistortion/oversample_8x_on.png create mode 100644 src/gui/widgets/Draggable.cpp diff --git a/cmake/modules/PluginList.cmake b/cmake/modules/PluginList.cmake index 009679533..7a6b266cf 100644 --- a/cmake/modules/PluginList.cmake +++ b/cmake/modules/PluginList.cmake @@ -61,6 +61,7 @@ SET(LMMS_PLUGIN_LIST Sf2Player Sfxr Sid + SlewDistortion SlicerT SpectrumAnalyzer StereoEnhancer diff --git a/include/Draggable.h b/include/Draggable.h new file mode 100644 index 000000000..957cb393c --- /dev/null +++ b/include/Draggable.h @@ -0,0 +1,76 @@ +/* + * Draggable.h + * + * Copyright (c) 2025 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GUI_DRAGGABLE_H +#define LMMS_GUI_DRAGGABLE_H + +#include "FloatModelEditorBase.h" + +namespace lmms::gui +{ + +/** + * @brief A pixmap that can be dragged from one location to another to control a FloatModel + */ +class LMMS_EXPORT Draggable : public FloatModelEditorBase +{ + Q_OBJECT + +public: + using FloatModelEditorBase::DirectionOfManipulation; + + /** + * @param directionOfManipulation Direction the user can drag the control + * @param floatModel Pointer to the FloatModel this draggable modifies + * @param pixmap The pixmap shown for this handle + * @param pointA Starting pixel position (Y if vertical, X if horizontal) + * @param pointB Ending pixel position (Y if vertical, X if horizontal) + * @param parent Parent QWidget + */ + Draggable(DirectionOfManipulation directionOfManipulation, + FloatModel* floatModel, const QPixmap& pixmap, int pointA, int pointB, QWidget* parent = nullptr); + + QSize sizeHint() const override; + void setPixmap(const QPixmap& pixmap); + void setDefaultValPixmap(const QPixmap& pixmap, float value = 0.f); + +protected: + void paintEvent(QPaintEvent* event) override; + void mouseMoveEvent(QMouseEvent* me) override; + +protected slots: + void handleMovement(); + +private: + QPixmap m_pixmap; + QPixmap m_defaultValPixmap; + float m_pointA; + float m_pointB; + float m_defaultValue; + bool m_hasDefaultValPixmap; +}; + +} // namespace lmms::gui + +#endif // LMMS_GUI_DRAGGABLE_H diff --git a/include/FloatModelEditorBase.h b/include/FloatModelEditorBase.h index 72f1450de..458459c9f 100644 --- a/include/FloatModelEditorBase.h +++ b/include/FloatModelEditorBase.h @@ -85,13 +85,6 @@ protected: void leaveEvent(QEvent *event) override; virtual float getValue(const QPoint & p); - -private slots: - virtual void enterValue(); - void friendlyUpdate(); - void toggleScale(); - -private: virtual QString displayValue() const; void doConnections() override; @@ -114,6 +107,11 @@ private: bool m_buttonPressed; DirectionOfManipulation m_directionOfManipulation; + +private slots: + virtual void enterValue(); + void friendlyUpdate(); + void toggleScale(); }; } // namespace lmms::gui diff --git a/include/OversamplingHelpers.h b/include/OversamplingHelpers.h new file mode 100755 index 000000000..934d8e894 --- /dev/null +++ b/include/OversamplingHelpers.h @@ -0,0 +1,249 @@ +/* + * OversamplingHelpers.h + * + * Copyright (c) 2025 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_OVERSAMPLING_HELPERS_H +#define LMMS_OVERSAMPLING_HELPERS_H + +#include +#include + +#include + +#ifdef __SSE2__ +#include +#include +#else +#include +#include +#endif + + +inline constexpr float HIIR_DEFAULT_PASSBAND = 19600; +inline constexpr int HIIR_DEFAULT_MAX_COEFS = 8; + +namespace lmms +{ + +template +class Upsampler +{ +public: + void reset() + { + float bw = 0.5f - m_passband / m_sampleRate; + + // Stage 1 + double coefsFirst[s_firstCoefCount]; + hiir::PolyphaseIir2Designer::compute_coefs_spec_order_tbw(coefsFirst, s_firstCoefCount, bw); + m_upsampleFirst.set_coefs(coefsFirst); + m_upsampleFirst.clear_buffers(); + bw = (bw + 0.5f) * 0.5f; + + // Stage 2 + double coefsSecond[s_secondCoefCount]; + hiir::PolyphaseIir2Designer::compute_coefs_spec_order_tbw(coefsSecond, s_secondCoefCount, bw); + m_upsampleSecond.set_coefs(coefsSecond); + m_upsampleSecond.clear_buffers(); + bw = (bw + 0.5f) * 0.5f; + + // Remaining stages + for (int i = 0; i < m_stages - 2; ++i) + { + double coefsRest[s_restCoefCount]; + hiir::PolyphaseIir2Designer::compute_coefs_spec_order_tbw(coefsRest, s_restCoefCount, bw); + m_upsampleRest[i].set_coefs(coefsRest); + m_upsampleRest[i].clear_buffers(); + bw = (bw + 0.5f) * 0.5f; + } + } + + void setup(int stages, float sampleRate, float passband = HIIR_DEFAULT_PASSBAND) + { + assert(stages <= MaxStages); + m_stages = stages; + m_sampleRate = sampleRate; + m_passband = passband; + reset(); + } + + // expects `2 ^ m_stages` elements for the output + void processSample(float* outSamples, float inSample) + { + int total = 1 << m_stages; + outSamples[0] = inSample; + int gap1 = total / 2; + + if (m_stages >= 1) + { + m_upsampleFirst.process_sample(outSamples[0], outSamples[gap1], outSamples[0]); + } + + if (m_stages >= 2) + { + int gap2 = gap1 / 2; + m_upsampleSecond.process_sample(outSamples[0], outSamples[gap2], outSamples[0]); + m_upsampleSecond.process_sample(outSamples[gap1], outSamples[gap1 + gap2], outSamples[gap1]); + } + + for (int i = 2; i < m_stages; ++i) + { + int count = 1 << i; + int gap = total / count; + for (int j = 0; j < count; ++j) + { + int temp = j * gap; + m_upsampleRest[i - 2].process_sample(outSamples[temp], outSamples[temp + gap / 2], outSamples[temp]); + } + } + } + + int getStages() const { return m_stages; } + float getSampleRate() const { return m_sampleRate; } + float getPassband() const { return m_passband; } + + void setStages(int stages) { m_stages = stages; reset(); } + void setSampleRate(float sampleRate) { m_sampleRate = sampleRate; reset(); } + void setPassband(float passband) { m_passband = passband; reset(); } + +private: + static constexpr int s_firstCoefCount = MaxCoefs; + static constexpr int s_secondCoefCount = std::max(MaxCoefs / 2, 2); + static constexpr int s_restCoefCount = std::max(MaxCoefs / 4, 2); +#ifdef __SSE2__ + hiir::Upsampler2xSse m_upsampleFirst; + hiir::Upsampler2xSse m_upsampleSecond; + std::array, MaxStages - 2> m_upsampleRest; +#else + hiir::Upsampler2xFpu m_upsampleFirst; + hiir::Upsampler2xFpu m_upsampleSecond; + std::array, MaxStages - 2> m_upsampleRest; +#endif + + int m_stages = 0; + float m_sampleRate = 44100; + float m_passband = HIIR_DEFAULT_PASSBAND; +}; + + +template +class Downsampler +{ +public: + void reset() + { + float bw = 0.5f - m_passband / m_sampleRate; + + // Stage 1 + double coefsFirst[s_firstCoefCount]; + hiir::PolyphaseIir2Designer::compute_coefs_spec_order_tbw(coefsFirst, s_firstCoefCount, bw); + m_downsampleFirst.set_coefs(coefsFirst); + m_downsampleFirst.clear_buffers(); + bw = (bw + 0.5f) * 0.5f; + + // Stage 2 + double coefsSecond[s_secondCoefCount]; + hiir::PolyphaseIir2Designer::compute_coefs_spec_order_tbw(coefsSecond, s_secondCoefCount, bw); + m_downsampleSecond.set_coefs(coefsSecond); + m_downsampleSecond.clear_buffers(); + bw = (bw + 0.5f) * 0.5f; + + // Remaining stages + for (int i = 0; i < m_stages - 2; ++i) + { + double coefsRest[s_restCoefCount]; + hiir::PolyphaseIir2Designer::compute_coefs_spec_order_tbw(coefsRest, s_restCoefCount, bw); + m_downsampleRest[i].set_coefs(coefsRest); + m_downsampleRest[i].clear_buffers(); + bw = (bw + 0.5f) * 0.5f; + } + } + + void setup(int stages, float sampleRate, float passband = HIIR_DEFAULT_PASSBAND) + { + assert(stages <= MaxStages); + m_stages = stages; + m_sampleRate = sampleRate; + m_passband = passband; + reset(); + } + + // expects `2 ^ m_stages` elements for the input + float processSample(float* inSamples) + { + for (int i = m_stages - 1; i >= 2; --i) + { + for (int j = 0; j < 1 << i; ++j) + { + inSamples[j] = m_downsampleRest[i - 2].process_sample(&inSamples[j * 2]); + } + } + + if (m_stages >= 2) + { + for (int j = 0; j < 2; ++j) + { + inSamples[j] = m_downsampleSecond.process_sample(&inSamples[j * 2]); + } + } + + if (m_stages >= 1) + { + inSamples[0] = m_downsampleFirst.process_sample(&inSamples[0]); + } + + return inSamples[0]; + } + + int getStages() const { return m_stages; } + float getSampleRate() const { return m_sampleRate; } + float getPassband() const { return m_passband; } + + void setStages(int stages) { m_stages = stages; reset(); } + void setSampleRate(float sampleRate) { m_sampleRate = sampleRate; reset(); } + void setPassband(float passband) { m_passband = passband; reset(); } + +private: + static constexpr int s_firstCoefCount = MaxCoefs; + static constexpr int s_secondCoefCount = std::max(MaxCoefs / 2, 2); + static constexpr int s_restCoefCount = std::max(MaxCoefs / 4, 2); +#ifdef __SSE2__ + hiir::Downsampler2xSse m_downsampleFirst; + hiir::Downsampler2xSse m_downsampleSecond; + std::array, MaxStages - 2> m_downsampleRest; +#else + hiir::Downsampler2xFpu m_downsampleFirst; + hiir::Downsampler2xFpu m_downsampleSecond; + std::array, MaxStages - 2> m_downsampleRest; +#endif + + int m_stages = 0; + float m_sampleRate = 44100; + float m_passband = HIIR_DEFAULT_PASSBAND; +}; + + +} // namespace lmms + +#endif // LMMS_OVERSAMPLING_HELPERS_H + diff --git a/include/lmms_math.h b/include/lmms_math.h index 5129743de..cefe1265b 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -36,6 +36,10 @@ #include "lmms_constants.h" +#ifdef __SSE2__ +#include +#endif + namespace lmms { @@ -280,6 +284,77 @@ private: T m_b; }; +#ifdef __SSE2__ +// exp approximation for SSE2: https://stackoverflow.com/a/47025627/5759631 +// Maximum relative error of 1.72863156e-3 on [-87.33654, 88.72283] +inline __m128 fastExp(__m128 x) +{ + __m128 f, p, r; + __m128i t, j; + const __m128 a = _mm_set1_ps (12102203.0f); /* (1 << 23) / log(2) */ + const __m128i m = _mm_set1_epi32 (0xff800000); /* mask for integer bits */ + const __m128 ttm23 = _mm_set1_ps (1.1920929e-7f); /* exp2(-23) */ + const __m128 c0 = _mm_set1_ps (0.3371894346f); + const __m128 c1 = _mm_set1_ps (0.657636276f); + const __m128 c2 = _mm_set1_ps (1.00172476f); + + t = _mm_cvtps_epi32 (_mm_mul_ps (a, x)); + j = _mm_and_si128 (t, m); /* j = (int)(floor (x/log(2))) << 23 */ + t = _mm_sub_epi32 (t, j); + f = _mm_mul_ps (ttm23, _mm_cvtepi32_ps (t)); /* f = (x/log(2)) - floor (x/log(2)) */ + p = c0; /* c0 */ + p = _mm_mul_ps (p, f); /* c0 * f */ + p = _mm_add_ps (p, c1); /* c0 * f + c1 */ + p = _mm_mul_ps (p, f); /* (c0 * f + c1) * f */ + p = _mm_add_ps (p, c2); /* p = (c0 * f + c1) * f + c2 ~= 2^f */ + r = _mm_castsi128_ps (_mm_add_epi32 (j, _mm_castps_si128 (p))); /* r = p * 2^i*/ + return r; +} + +// Lost Robot's SSE2 adaptation of Kari's vectorized log approximation: https://stackoverflow.com/a/65537754/5759631 +// Maximum relative error of 7.922410e-4 on [1.0279774e-38f, 3.4028235e+38f] +inline __m128 fastLog(__m128 a) +{ + __m128i aInt = _mm_castps_si128(a); + __m128i e = _mm_sub_epi32(aInt, _mm_set1_epi32(0x3f2aaaab)); + e = _mm_and_si128(e, _mm_set1_epi32(0xff800000)); + __m128i subtr = _mm_sub_epi32(aInt, e); + __m128 m = _mm_castsi128_ps(subtr); + __m128 i = _mm_mul_ps(_mm_cvtepi32_ps(e), _mm_set1_ps(1.19209290e-7f)); + __m128 f = _mm_sub_ps(m, _mm_set1_ps(1.0f)); + __m128 s = _mm_mul_ps(f, f); + __m128 r = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(0.230836749f), f), _mm_set1_ps(-0.279208571f)); + __m128 t = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(0.331826031f), f), _mm_set1_ps(-0.498910338f)); + r = _mm_add_ps(_mm_mul_ps(r, s), t); + r = _mm_add_ps(_mm_mul_ps(r, s), f); + r = _mm_add_ps(_mm_mul_ps(i, _mm_set1_ps(0.693147182f)), r); + return r; +} + +inline __m128 sse2Abs(__m128 x) +{ + return _mm_and_ps(x, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)));// clear sign bit +} + +inline __m128 sse2Floor(__m128 x) +{ + __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(x)); // trunc toward 0 + __m128 needs_correction = _mm_cmplt_ps(x, t); // checks if x < trunc + return _mm_sub_ps(t, _mm_and_ps(needs_correction, _mm_set1_ps(1.0f))); +} + +inline __m128 sse2Round(__m128 x) +{ + __m128 sign_mask = _mm_cmplt_ps(x, _mm_setzero_ps());// checks if x < 0 + __m128 bias_pos = _mm_set1_ps(0.5f); + __m128 bias_neg = _mm_set1_ps(-0.5f); + __m128 bias = _mm_or_ps(_mm_and_ps(sign_mask, bias_neg), _mm_andnot_ps(sign_mask, bias_pos)); + __m128 y = _mm_add_ps(x, bias); + return _mm_cvtepi32_ps(_mm_cvttps_epi32(y)); +} + +#endif // __SSE2__ + } // namespace lmms #endif // LMMS_MATH_H diff --git a/plugins/SlewDistortion/CMakeLists.txt b/plugins/SlewDistortion/CMakeLists.txt new file mode 100755 index 000000000..e5e2f695b --- /dev/null +++ b/plugins/SlewDistortion/CMakeLists.txt @@ -0,0 +1,4 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(slewdistortion SlewDistortion.cpp SlewDistortionControls.cpp SlewDistortionControlDialog.cpp MOCFILES SlewDistortion.h SlewDistortionControls.h SlewDistortionControlDialog.h EMBEDDED_RESOURCES *.png) +TARGET_LINK_LIBRARIES(slewdistortion hiir) diff --git a/plugins/SlewDistortion/SlewDistortion.cpp b/plugins/SlewDistortion/SlewDistortion.cpp new file mode 100755 index 000000000..dca453b42 --- /dev/null +++ b/plugins/SlewDistortion/SlewDistortion.cpp @@ -0,0 +1,720 @@ +/* + * SlewDistortion.cpp + * + * Copyright (c) 2025 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "SlewDistortion.h" + +#include "embed.h" +#include "plugin_export.h" + +namespace lmms +{ + +extern "C" +{ +Plugin::Descriptor PLUGIN_EXPORT slewdistortion_plugin_descriptor = +{ + LMMS_STRINGIFY(PLUGIN_NAME), + "Slew Distortion", + QT_TRANSLATE_NOOP("PluginBrowser", "A 2-band distortion and slew rate limiter plugin."), + "Lost Robot ", + 0x0100, + Plugin::Type::Effect, + new PluginPixmapLoader("logo"), + nullptr, + nullptr, +}; +} + + +SlewDistortion::SlewDistortion(Model* parent, const Descriptor::SubPluginFeatures::Key* key) : + Effect(&slewdistortion_plugin_descriptor, parent, key), + m_sampleRate(Engine::audioEngine()->outputSampleRate()), + m_lp(m_sampleRate), + m_hp(m_sampleRate), + m_slewdistortionControls(this) +{ + connect(Engine::audioEngine(), &AudioEngine::sampleRateChanged, this, &SlewDistortion::changeSampleRate); + changeSampleRate(); +} + + +#ifdef __SSE2__ +Effect::ProcessStatus SlewDistortion::processImpl(SampleFrame* buf, const fpp_t frames) +{ + const float d = dryLevel(); + const float w = wetLevel(); + + const int oversampling = m_slewdistortionControls.m_oversamplingModel.value(); + const int oversampleVal = 1 << oversampling; + if (oversampleVal != m_oldOversampleVal) + { + m_oldOversampleVal = oversampleVal; + changeSampleRate(); + } + + const SlewDistortionType distType1 = static_cast(m_slewdistortionControls.m_distType1Model.value()); + const SlewDistortionType distType2 = static_cast(m_slewdistortionControls.m_distType2Model.value()); + const float drive1 = dbfsToAmp(m_slewdistortionControls.m_drive1Model.value()); + const float drive2 = dbfsToAmp(m_slewdistortionControls.m_drive2Model.value()); + const float slewUp1 = dbfsToAmp(m_slewdistortionControls.m_slewUp1Model.value()) / oversampleVal; + const float slewUp2 = dbfsToAmp(m_slewdistortionControls.m_slewUp2Model.value()) / oversampleVal; + const float slewDown1 = dbfsToAmp(m_slewdistortionControls.m_slewDown1Model.value()) / oversampleVal; + const float slewDown2 = dbfsToAmp(m_slewdistortionControls.m_slewDown2Model.value()) / oversampleVal; + const float bias1 = m_slewdistortionControls.m_bias1Model.value(); + const float bias2 = m_slewdistortionControls.m_bias2Model.value(); + const float warp1 = m_slewdistortionControls.m_warp1Model.value(); + const float warp2 = m_slewdistortionControls.m_warp2Model.value(); + const float crush1 = dbfsToAmp(m_slewdistortionControls.m_crush1Model.value()); + const float crush2 = dbfsToAmp(m_slewdistortionControls.m_crush2Model.value()); + const float attack1 = msToCoeff(m_slewdistortionControls.m_attack1Model.value()); + const float attack2 = msToCoeff(m_slewdistortionControls.m_attack2Model.value()); + const float attackInv1 = 1.f - attack1; + const float attackInv2 = 1.f - attack2; + const float release1 = msToCoeff(m_slewdistortionControls.m_release1Model.value()); + const float release2 = msToCoeff(m_slewdistortionControls.m_release2Model.value()); + const float releaseInv1 = 1.f - release1; + const float releaseInv2 = 1.f - release2; + const float dynamics1 = m_slewdistortionControls.m_dynamics1Model.value(); + const float dynamics2 = m_slewdistortionControls.m_dynamics2Model.value(); + const float dynamicSlew1 = m_slewdistortionControls.m_dynamicSlew1Model.value(); + const float dynamicSlew2 = m_slewdistortionControls.m_dynamicSlew2Model.value(); + const float outVol1 = dbfsToAmp(m_slewdistortionControls.m_outVol1Model.value()); + const float outVol2 = dbfsToAmp(m_slewdistortionControls.m_outVol2Model.value()); + const float split = m_slewdistortionControls.m_splitModel.value(); + const bool dcRemove = m_slewdistortionControls.m_dcRemoveModel.value(); + const bool multiband = m_slewdistortionControls.m_multibandModel.value(); + const float mix1 = m_slewdistortionControls.m_mix1Model.value(); + const float mix2 = m_slewdistortionControls.m_mix2Model.value(); + const bool slewLink1 = m_slewdistortionControls.m_slewLink1Model.value(); + const bool slewLink2 = m_slewdistortionControls.m_slewLink2Model.value(); + + const __m128 drive = _mm_set_ps(drive2, drive2, drive1, drive1); + const __m128 slewUp = _mm_set_ps(slewUp2, slewUp2, slewUp1, slewUp1); + const __m128 slewDown = _mm_set_ps(slewDown2, slewDown2, slewDown1, slewDown1); + const __m128 warp = _mm_set_ps(warp2, warp2, warp1, warp1); + const __m128 crush = _mm_set_ps(crush2, crush2, crush1, crush1); + const __m128 outVol = _mm_set_ps(outVol2, outVol2, outVol1, outVol1); + const __m128 attack = _mm_set_ps(attack2, attack2, attack1, attack1); + const __m128 attackInv = _mm_set_ps(attackInv2, attackInv2, attackInv1, attackInv1); + const __m128 release = _mm_set_ps(release2, release2, release1, release1); + const __m128 releaseInv = _mm_set_ps(releaseInv2, releaseInv2, releaseInv1, releaseInv1); + const __m128 dynamics = _mm_set_ps(dynamics2, dynamics2, dynamics1, dynamics1); + const __m128 dynamicSlew = _mm_set_ps(dynamicSlew2, dynamicSlew2, dynamicSlew1, dynamicSlew1); + const __m128 mix = _mm_set_ps(mix2, mix2, mix1, mix1); + const __m128 minFloor = _mm_set1_ps(SLEW_DISTORTION_MIN_FLOOR); + const int link1Mask = -static_cast(slewLink1); + const int link2Mask = -static_cast(slewLink2); + const __m128 slewLinkMask = _mm_castsi128_ps(_mm_set_epi32(link2Mask, link2Mask, link1Mask, link1Mask)); + + const __m128 zero = _mm_setzero_ps(); + const __m128 one = _mm_set1_ps(1.0f); + + if (m_slewdistortionControls.m_splitModel.isValueChanged()) + { + m_lp.setLowpass(split); + m_hp.setHighpass(split); + } + + for (fpp_t f = 0; f < frames; ++f) + { + // interpolate bias to remove crackling when moving the parameter + m_trueBias1 = m_biasInterpCoef * m_trueBias1 + (1.f - m_biasInterpCoef) * bias1; + m_trueBias2 = m_biasInterpCoef * m_trueBias2 + (1.f - m_biasInterpCoef) * bias2; + const __m128 bias = _mm_set_ps(m_trueBias2, m_trueBias2, m_trueBias1, m_trueBias1); + + if (oversampleVal > 1) + { + m_upsampler[0].processSample(m_overOuts[0].data(), buf[f][0]); + m_upsampler[1].processSample(m_overOuts[1].data(), buf[f][1]); + } + else + { + m_overOuts[0][0] = buf[f][0]; + m_overOuts[1][0] = buf[f][1]; + } + + for (int overSamp = 0; overSamp < oversampleVal; ++overSamp) + { + alignas(16) std::array inArr = {0}; + if (multiband) + { + inArr[0] = m_hp.update(m_overOuts[0][overSamp], 0); + inArr[1] = m_hp.update(m_overOuts[1][overSamp], 1); + inArr[2] = m_lp.update(m_overOuts[0][overSamp], 0); + inArr[3] = m_lp.update(m_overOuts[1][overSamp], 1); + } + else + { + inArr[0] = m_overOuts[0][overSamp]; + inArr[1] = m_overOuts[1][overSamp]; + inArr[2] = 0; + inArr[3] = 0; + } + + __m128 in = _mm_load_ps(&inArr[0]); + __m128 absIn = sse2Abs(in); + + // store volume for display + _mm_store_ps(&m_inPeakDisplay[0], _mm_max_ps(_mm_load_ps(&m_inPeakDisplay[0]), _mm_mul_ps(absIn, drive))); + + __m128 inEnv = _mm_load_ps(&m_inEnv[0]); + __m128 slewOut = _mm_load_ps(&m_slewOut[0]); + + // apply attack and release to envelope follower + __m128 cmp = _mm_cmpgt_ps(absIn, inEnv); + __m128 envRise = _mm_add_ps(_mm_mul_ps(inEnv, attack), _mm_mul_ps(absIn, attackInv)); + __m128 envFall = _mm_add_ps(_mm_mul_ps(inEnv, release), _mm_mul_ps(absIn, releaseInv)); + inEnv = _mm_or_ps(_mm_and_ps(cmp, envRise), _mm_andnot_ps(cmp, envFall)); + inEnv = _mm_max_ps(inEnv, minFloor); + + // this is the input signal's slew rate + __m128 rate = _mm_sub_ps(in, slewOut); + + __m128 scaledLog = _mm_mul_ps(dynamicSlew, fastLog(inEnv)); + // clamp to [-80.0f, 80.0f] since float std::exp breaks outside of those bounds + __m128 clampedScaledLog = _mm_max_ps(_mm_min_ps(scaledLog, _mm_set1_ps(80.0f)), _mm_set1_ps(-80.0f)); + __m128 slewMult = fastExp(clampedScaledLog); + + // determine whether we should use the slew up or slew down parameter + __m128 finalMask = _mm_or_ps(_mm_cmpge_ps(rate, zero), slewLinkMask); + __m128 finalSlew = _mm_or_ps(_mm_and_ps(finalMask, _mm_mul_ps(slewUp, slewMult)), + _mm_andnot_ps(finalMask, _mm_mul_ps(slewDown, slewMult))); + + __m128 clampedRate = _mm_max_ps(_mm_sub_ps(zero, finalSlew), _mm_min_ps(rate, finalSlew)); + slewOut = _mm_add_ps(slewOut, clampedRate); + + // apply drive and bias + __m128 biasedIn = _mm_add_ps(_mm_mul_ps(slewOut, drive), bias); + + // apply warp and crush + // distIn = (biasedIn - std::copysign(warp[i] / crush[i], biasedIn)) / (1.f - warp[i]); + __m128 signBiasedIn = _mm_and_ps(biasedIn, _mm_castsi128_ps(_mm_set1_epi32(0x80000000))); + __m128 warpOverCrush = _mm_div_ps(warp, crush); + __m128 copysignWarpOverCrush = _mm_or_ps(warpOverCrush, signBiasedIn); + __m128 distIn = _mm_div_ps(_mm_sub_ps(biasedIn, copysignWarpOverCrush), _mm_sub_ps(one, warp)); + + alignas(16) std::array distInArr; + _mm_store_ps(&distInArr[0], distIn); + alignas(16) std::array distOutArr; + + // if both bands have the same distortion type, we can process all four channels simultaneously + // otherwise we have to do two at a time + int loopCount = (distType1 == distType2 || !multiband) ? 1 : 2; + + for (int pair = 0; pair < loopCount; ++pair) + { + SlewDistortionType currentDistType = (pair == 0) ? distType1 : distType2; + + __m128 distInFull = _mm_load_ps(&distInArr[0]); + __m128 distOutFull; + + // switch-case applies the distortion to the full set of 4 values + switch (currentDistType) + { + case SlewDistortionType::HardClip:// Hard Clip => clamp(x, -1, 1) + { + __m128 minVal = _mm_set1_ps(-1.0f); + __m128 maxVal = one; + distOutFull = _mm_max_ps(_mm_min_ps(distInFull, maxVal), minVal); + break; + } + case SlewDistortionType::Tanh: // Tanh => 2 / (1 + exp(-2x)) - 1 + { + // clamp to [-80.0f, 80.0f] since float std::exp breaks outside of those bounds + __m128 clampedInput = _mm_max_ps(_mm_min_ps(_mm_mul_ps(_mm_set1_ps(-2.0f), + distInFull), _mm_set1_ps(80.0f)), _mm_set1_ps(-80.0f)); + __m128 expResult = fastExp(clampedInput); + distOutFull = _mm_sub_ps(_mm_div_ps(_mm_set1_ps(2.0f), _mm_add_ps(one, expResult)), one); + break; + } + case SlewDistortionType::FastSoftClip1: // Fast Soft Clip 1 => x / (1 + x^2 / 4) + { + __m128 temp = _mm_max_ps(_mm_min_ps(distInFull, _mm_set1_ps(2.f)), _mm_set1_ps(-2.f));// clamp + distOutFull = _mm_div_ps(temp, _mm_add_ps(one, + _mm_mul_ps(_mm_set1_ps(0.25f), _mm_mul_ps(temp, temp)))); + break; + } + case SlewDistortionType::FastSoftClip2: // Fast Soft Clip 2 => x - (4/27) * x^3 + { + __m128 temp = _mm_max_ps(_mm_min_ps(distInFull, _mm_set1_ps(1.5f)), _mm_set1_ps(-1.5f));// clamp + distOutFull = _mm_sub_ps(temp, _mm_mul_ps(_mm_set1_ps(4.f / 27.f), + _mm_mul_ps(_mm_mul_ps(temp, temp), temp))); + break; + } + case SlewDistortionType::Sinusoidal: // Sinusoidal => sin(x) + { + // SSE2 sine approximation I created + __m128 pi = _mm_set1_ps(std::numbers::pi_v); + __m128 piOverTwo = _mm_set1_ps(std::numbers::pi_v * 0.5f); + __m128 tau = _mm_set1_ps(std::numbers::pi_v * 2.f); + + __m128 distMinusPiOverTwo = _mm_sub_ps(distInFull, piOverTwo); + __m128 divByTwoPi = _mm_div_ps(distMinusPiOverTwo, tau); + + // SSE2 floor replacement + __m128 floorDivByTwoPi = sse2Floor(divByTwoPi); + + // x mod 2pi = x - floor(x / 2pi) * 2pi + __m128 floorMulTwoPi = _mm_mul_ps(floorDivByTwoPi, tau); + __m128 modInput = _mm_sub_ps(distMinusPiOverTwo, floorMulTwoPi); + + // abs(in - pi) - pi/2 + __m128 x = _mm_sub_ps(sse2Abs(_mm_sub_ps(modInput, pi)), piOverTwo); + + // polynomial sine approximation + // sin(x) ≈ x - x^3 / 6 + x^5 / 120 + __m128 x2 = _mm_mul_ps(x, x); + __m128 x3 = _mm_mul_ps(x2, x); + __m128 x5 = _mm_mul_ps(x3, x2); + __m128 sinApprox = _mm_sub_ps(x, _mm_mul_ps(x3, _mm_set1_ps(1.0f / 6.0f))); + distOutFull = _mm_add_ps(sinApprox, _mm_mul_ps(x5, _mm_set1_ps(1.0f / 120.0f))); + break; + } + case SlewDistortionType::Foldback: // Foldback => |(|x - 1| mod 4) - 2| - 1 = |2 - |(x - 1) - 4 * floor((x - 1) / 4)|| - 1 + { + __m128 four = _mm_set1_ps(4.0f); + __m128 distInMinusOne = _mm_sub_ps(distInFull, one); + __m128 divByFour = _mm_div_ps(distInMinusOne, four); + + // floor + __m128 floorOverFour = sse2Floor(divByFour); + + distOutFull = _mm_sub_ps(sse2Abs(_mm_sub_ps(_mm_sub_ps( + distInMinusOne, _mm_mul_ps(floorOverFour, four)), _mm_set1_ps(2.0f))), one); + break; + } + case SlewDistortionType::FullRectify: // |x| + { + distOutFull = sse2Abs(distInFull); + break; + } + case SlewDistortionType::SmoothRectify: // sqrt(x^2 + 0.04) - 0.2 + { + distOutFull = _mm_sub_ps(_mm_sqrt_ps(_mm_add_ps(_mm_mul_ps(distInFull, distInFull), + _mm_set1_ps(0.04f))), _mm_set1_ps(0.2f)); + break; + } + case SlewDistortionType::HalfRectify: // max(0, x) + { + distOutFull = _mm_max_ps(_mm_setzero_ps(), distInFull); + break; + } + case SlewDistortionType::Bitcrush: // round(x / drive * scale) / scale + { + // scale = 16 / drive + __m128 scale = _mm_div_ps(_mm_set1_ps(16.f), drive); + __m128 scaledVal = _mm_mul_ps(_mm_div_ps(distInFull, drive), scale); + + // round to nearest, half away from zero + __m128 rounded = sse2Round(scaledVal); + + distOutFull = _mm_div_ps(rounded, scale); + break; + } + default: + { + distOutFull = distInFull; + break; + } + } + + if (loopCount == 1)// we can store all four simultaneously + { + _mm_store_ps(&distOutArr[0], distOutFull); + break; + } + else// need to store two at a time + { + if (pair == 0) + { + // for elements 0 and 1 + _mm_storel_pi(reinterpret_cast<__m64*>(&distOutArr[0]), distOutFull); + } + else + { + // for elements 2 and 3 + _mm_storeh_pi(reinterpret_cast<__m64*>(&distOutArr[2]), distOutFull); + } + } + } + + __m128 distOut = _mm_load_ps(&distOutArr[0]); + + // (1 - warp) * distOut + std::copysign(warp, biasedIn) + __m128 distOutScaled = _mm_add_ps(_mm_mul_ps(distOut, _mm_sub_ps(one, warp)), _mm_or_ps(warp, signBiasedIn)); + + // if (abs(biasedIn) < warp / crush) {distOut = biasedIn * crush;} + __m128 absBiasedIn = sse2Abs(biasedIn); + __m128 condition = _mm_cmplt_ps(absBiasedIn, _mm_div_ps(warp, crush)); + __m128 biasedInCrush = _mm_mul_ps(biasedIn, crush); + + distOut = _mm_or_ps(_mm_and_ps(condition, biasedInCrush), _mm_andnot_ps(condition, distOutScaled)); + + // DC offset calculation + __m128 dcOffset = _mm_load_ps(&m_dcOffset[0]); + __m128 dcCoeff = _mm_set1_ps(m_dcCoeff); + dcOffset = _mm_add_ps(_mm_mul_ps(dcOffset, dcCoeff), _mm_mul_ps(distOut, _mm_sub_ps(one, dcCoeff))); + + __m128 distOutMinusDC = _mm_sub_ps(distOut, dcOffset); + + // even with DC offset removal disabled, we should still apply it for the envelope follower + __m128 outEnv = _mm_load_ps(&m_outEnv[0]); + __m128 absOut = sse2Abs(distOutMinusDC); + + cmp = _mm_cmpgt_ps(absOut, outEnv); + __m128 outEnvRise = _mm_add_ps(_mm_mul_ps(outEnv, attack), _mm_mul_ps(absOut, attackInv)); + __m128 outEnvFall = _mm_add_ps(_mm_mul_ps(outEnv, release), _mm_mul_ps(absOut, releaseInv)); + outEnv = _mm_max_ps(_mm_or_ps(_mm_and_ps(cmp, outEnvRise), _mm_andnot_ps(cmp, outEnvFall)), minFloor); + + // remove DC + __m128 finalDistOut = (dcRemove) ? distOutMinusDC : distOut; + + // crossfade between a multiplier of 1 and (inEnv/outEnv) for dynamics feature + __m128 distDyn = _mm_mul_ps(finalDistOut, _mm_add_ps(one, + _mm_mul_ps(_mm_sub_ps(_mm_div_ps(inEnv, outEnv), one), dynamics))); + + // apply mix + __m128 outFinal = _mm_mul_ps(_mm_add_ps(in, _mm_mul_ps(mix, _mm_sub_ps(distDyn, in))), outVol); + + // store volume for display + __m128 outAbs = sse2Abs(outFinal); + _mm_store_ps(&m_outPeakDisplay[0], _mm_max_ps(_mm_load_ps(&m_outPeakDisplay[0]), outAbs)); + + // write updated stuff back into member variables + _mm_store_ps(&m_inEnv[0], inEnv); + _mm_store_ps(&m_slewOut[0], slewOut); + _mm_store_ps(&m_dcOffset[0], dcOffset); + _mm_store_ps(&m_outEnv[0], outEnv); + + alignas(16) std::array outArr; + _mm_store_ps(&outArr[0], outFinal); + + m_overOuts[0][overSamp] = outArr[0] + outArr[2]; + m_overOuts[1][overSamp] = outArr[1] + outArr[3]; + } + + std::array s; + if (oversampleVal > 1) + { + s[0] = m_downsampler[0].processSample(m_overOuts[0].data()); + s[1] = m_downsampler[1].processSample(m_overOuts[1].data()); + } + else + { + s[0] = m_overOuts[0][0]; + s[1] = m_overOuts[1][0]; + } + + buf[f][0] = d * buf[f][0] + w * s[0]; + buf[f][1] = d * buf[f][1] + w * s[1]; + } + + return ProcessStatus::ContinueIfNotQuiet; +} + + + +#else +Effect::ProcessStatus SlewDistortion::processImpl(SampleFrame* buf, const fpp_t frames) +{ + const float d = dryLevel(); + const float w = wetLevel(); + + const int oversampling = m_slewdistortionControls.m_oversamplingModel.value(); + const int oversampleVal = 1 << oversampling; + if (oversampleVal != m_oldOversampleVal) + { + m_oldOversampleVal = oversampleVal; + changeSampleRate(); + } + + const SlewDistortionType distType1 = static_cast(m_slewdistortionControls.m_distType1Model.value()); + const SlewDistortionType distType2 = static_cast(m_slewdistortionControls.m_distType2Model.value()); + const float drive1 = dbfsToAmp(m_slewdistortionControls.m_drive1Model.value()); + const float drive2 = dbfsToAmp(m_slewdistortionControls.m_drive2Model.value()); + const float slewUp1 = dbfsToAmp(m_slewdistortionControls.m_slewUp1Model.value()) / oversampleVal; + const float slewUp2 = dbfsToAmp(m_slewdistortionControls.m_slewUp2Model.value()) / oversampleVal; + const float slewDown1 = dbfsToAmp(m_slewdistortionControls.m_slewDown1Model.value()) / oversampleVal; + const float slewDown2 = dbfsToAmp(m_slewdistortionControls.m_slewDown2Model.value()) / oversampleVal; + const float bias1 = m_slewdistortionControls.m_bias1Model.value(); + const float bias2 = m_slewdistortionControls.m_bias2Model.value(); + const float warp1 = m_slewdistortionControls.m_warp1Model.value(); + const float warp2 = m_slewdistortionControls.m_warp2Model.value(); + const float crush1 = dbfsToAmp(m_slewdistortionControls.m_crush1Model.value()); + const float crush2 = dbfsToAmp(m_slewdistortionControls.m_crush2Model.value()); + const float attack1 = msToCoeff(m_slewdistortionControls.m_attack1Model.value()); + const float attack2 = msToCoeff(m_slewdistortionControls.m_attack2Model.value()); + const float attackInv1 = 1.f - attack1; + const float attackInv2 = 1.f - attack2; + const float release1 = msToCoeff(m_slewdistortionControls.m_release1Model.value()); + const float release2 = msToCoeff(m_slewdistortionControls.m_release2Model.value()); + const float releaseInv1 = 1.f - release1; + const float releaseInv2 = 1.f - release2; + const float dynamics1 = m_slewdistortionControls.m_dynamics1Model.value(); + const float dynamics2 = m_slewdistortionControls.m_dynamics2Model.value(); + const float dynamicSlew1 = m_slewdistortionControls.m_dynamicSlew1Model.value(); + const float dynamicSlew2 = m_slewdistortionControls.m_dynamicSlew2Model.value(); + const float outVol1 = dbfsToAmp(m_slewdistortionControls.m_outVol1Model.value()); + const float outVol2 = dbfsToAmp(m_slewdistortionControls.m_outVol2Model.value()); + const float split = m_slewdistortionControls.m_splitModel.value(); + const bool dcRemove = m_slewdistortionControls.m_dcRemoveModel.value(); + const bool multiband = m_slewdistortionControls.m_multibandModel.value(); + const float mix1 = m_slewdistortionControls.m_mix1Model.value(); + const float mix2 = m_slewdistortionControls.m_mix2Model.value(); + const bool slewLink1 = m_slewdistortionControls.m_slewLink1Model.value(); + const bool slewLink2 = m_slewdistortionControls.m_slewLink2Model.value(); + + std::array in = {0}; + std::array out = {0}; + const std::array drive = {drive1, drive1, drive2, drive2}; + const std::array slewUp = {slewUp1, slewUp1, slewUp2, slewUp2}; + const std::array slewDown = {slewDown1, slewDown1, slewDown2, slewDown2}; + const std::array distType = {distType1, distType1, distType2, distType2}; + const std::array warp = {warp1, warp1, warp2, warp2}; + const std::array crush = {crush1, crush1, crush2, crush2}; + const std::array outVol = {outVol1, outVol1, outVol2, outVol2}; + const std::array attack = {attack1, attack1, attack2, attack2}; + const std::array attackInv = {attackInv1, attackInv1, attackInv2, attackInv2}; + const std::array release = {release1, release1, release2, release2}; + const std::array releaseInv = {releaseInv1, releaseInv1, releaseInv2, releaseInv2}; + const std::array dynamics = {dynamics1, dynamics1, dynamics2, dynamics2}; + const std::array dynamicSlew = {dynamicSlew1, dynamicSlew1, dynamicSlew2, dynamicSlew2}; + const std::array mix = {mix1, mix1, mix2, mix2}; + const std::array slewLink = {slewLink1, slewLink1, slewLink2, slewLink2}; + + if (m_slewdistortionControls.m_splitModel.isValueChanged()) + { + m_lp.setLowpass(split); + m_hp.setHighpass(split); + } + + for (fpp_t f = 0; f < frames; ++f) + { + // interpolate bias to remove crackling when moving the parameter + m_trueBias1 = m_biasInterpCoef * m_trueBias1 + (1.f - m_biasInterpCoef) * bias1; + m_trueBias2 = m_biasInterpCoef * m_trueBias2 + (1.f - m_biasInterpCoef) * bias2; + const std::array bias = {m_trueBias1, m_trueBias1, m_trueBias2, m_trueBias2}; + + if (oversampleVal > 1) + { + m_upsampler[0].processSample(m_overOuts[0].data(), buf[f][0]); + m_upsampler[1].processSample(m_overOuts[1].data(), buf[f][1]); + } + else + { + m_overOuts[0][0] = buf[f][0]; + m_overOuts[1][0] = buf[f][1]; + } + + for (int overSamp = 0; overSamp < oversampleVal; ++overSamp) + { + if (multiband) + { + in[0] = m_hp.update(m_overOuts[0][overSamp], 0); + in[1] = m_hp.update(m_overOuts[1][overSamp], 1); + in[2] = m_lp.update(m_overOuts[0][overSamp], 0); + in[3] = m_lp.update(m_overOuts[1][overSamp], 1); + } + else + { + in[0] = m_overOuts[0][overSamp]; + in[1] = m_overOuts[1][overSamp]; + in[2] = 0; + in[3] = 0; + } + + m_inPeakDisplay[0] = std::max(m_inPeakDisplay[0], std::abs(in[0] * drive[0])); + m_inPeakDisplay[1] = std::max(m_inPeakDisplay[1], std::abs(in[1] * drive[1])); + m_inPeakDisplay[2] = std::max(m_inPeakDisplay[2], std::abs(in[2] * drive[2])); + m_inPeakDisplay[3] = std::max(m_inPeakDisplay[3], std::abs(in[3] * drive[3])); + + for (int i = 0; i < 4 - !multiband * 2; ++i) { + const float absIn = std::abs(in[i]); + m_inEnv[i] = absIn > m_inEnv[i] ? m_inEnv[i] * attack[i] + absIn * attackInv[i] : m_inEnv[i] * release[i] + absIn * releaseInv[i]; + m_inEnv[i] = std::max(m_inEnv[i], SLEW_DISTORTION_MIN_FLOOR); + + float rate = in[i] - m_slewOut[i]; + float slewMult = dynamicSlew[i] ? std::pow(m_inEnv[i], dynamicSlew[i]) : 1.f; + const float trueSlew = ((rate >= 0 || slewLink[i]) ? slewUp[i] : slewDown[i]) * slewMult; + rate = std::clamp(rate, -trueSlew, trueSlew); + m_slewOut[i] = m_slewOut[i] + rate; + + float biasedIn = m_slewOut[i] * drive[i] + bias[i]; + float distIn = (biasedIn - std::copysign(warp[i] / crush[i], biasedIn)) / (1.f - warp[i]); + float distOut; + switch (static_cast(distType[i])) + { + case SlewDistortionType::HardClip: { + distOut = std::clamp(distIn, -1.f, 1.f); + break; + } + case SlewDistortionType::Tanh: { + const float temp = std::clamp(distIn, -40.f, 40.f); + distOut = 2.f / (1.f + std::exp(-2.f * temp)) - 1; + break; + } + case SlewDistortionType::FastSoftClip1: { + const float temp = std::clamp(distIn, -2.f, 2.f); + distOut = temp / (1 + 0.25f * temp * temp); + break; + } + case SlewDistortionType::FastSoftClip2: { + const float temp = std::clamp(distIn, -1.5f, 1.5f); + distOut = temp - (4.f / 27.f) * temp * temp * temp; + break; + } + case SlewDistortionType::Sinusoidal: { + // using a polynomial approximation so it matches with the SSE2 code + // x - x^3 / 6 + x^5 / 120 + float modInput = std::fmod(distIn - std::numbers::pi_v * 0.5f, 2.f * std::numbers::pi_v); + if (modInput < 0) {modInput += 2.f * std::numbers::pi_v;} + const float x = std::abs(modInput - std::numbers::pi_v) - std::numbers::pi_v * 0.5f; + const float x2 = x * x; + const float x3 = x2 * x; + const float x5 = x3 * x2; + distOut = x - (x3 / 6.0f) + (x5 / 120.0f); + break; + } + case SlewDistortionType::Foldback: { + distOut = std::abs(std::abs(std::fmod(distIn - 1.f, 4.f)) - 2.f) - 1.f; + break; + } + case SlewDistortionType::FullRectify: { + distOut = std::abs(distIn); + break; + } + case SlewDistortionType::SmoothRectify: + { + distOut = std::sqrt(distIn * distIn + 0.04f) - 0.2f; + break; + } + case SlewDistortionType::HalfRectify: + { + distOut = std::max(0.0f, distIn); + break; + } + case SlewDistortionType::Bitcrush: + { + const float scale = 16 / drive[i]; + distOut = std::round(distIn / drive[i] * scale) / scale; + break; + } + default: + { + distOut = distIn; + } + } + distOut = distOut * (1.f - warp[i]) + std::copysign(warp[i], biasedIn); + if (std::abs(biasedIn) < warp[i] / crush[i]) {distOut = biasedIn * crush[i];} + + m_dcOffset[i] = m_dcOffset[i] * m_dcCoeff + distOut * (1.f - m_dcCoeff); + + // even with DC offset removal disabled, we should still apply it for the envelope follower + const float absOut = std::abs(distOut - m_dcOffset[i]); + m_outEnv[i] = absOut > m_outEnv[i] ? m_outEnv[i] * attack[i] + absOut * attackInv[i] : m_outEnv[i] * release[i] + absOut * releaseInv[i]; + m_outEnv[i] = std::max(m_outEnv[i], SLEW_DISTORTION_MIN_FLOOR); + + if (dcRemove) { distOut -= m_dcOffset[i]; } + + distOut *= std::lerp(1.f, m_inEnv[i] / m_outEnv[i], dynamics[i]); + + out[i] = std::lerp(in[i], distOut, mix[i]) * outVol[i]; + } + + m_outPeakDisplay[0] = std::max(m_outPeakDisplay[0], std::abs(out[0])); + m_outPeakDisplay[1] = std::max(m_outPeakDisplay[1], std::abs(out[1])); + m_outPeakDisplay[2] = std::max(m_outPeakDisplay[2], std::abs(out[2])); + m_outPeakDisplay[3] = std::max(m_outPeakDisplay[3], std::abs(out[3])); + + m_overOuts[0][overSamp] = out[0] + out[2]; + m_overOuts[1][overSamp] = out[1] + out[3]; + } + + std::array s; + if (oversampleVal > 1) + { + s[0] = m_downsampler[0].processSample(m_overOuts[0].data()); + s[1] = m_downsampler[1].processSample(m_overOuts[1].data()); + } + else + { + s[0] = m_overOuts[0][0]; + s[1] = m_overOuts[1][0]; + } + + buf[f][0] = d * buf[f][0] + w * s[0]; + buf[f][1] = d * buf[f][1] + w * s[1]; + } + + return ProcessStatus::ContinueIfNotQuiet; +} +#endif + +void SlewDistortion::changeSampleRate() +{ + m_sampleRate = Engine::audioEngine()->outputSampleRate(); + const int oversampleStages = m_slewdistortionControls.m_oversamplingModel.value(); + const int oversampleVal = 1 << oversampleStages; + float sampleRateOver = m_sampleRate * oversampleVal; + + for (int i = 0; i < 2; ++i) + { + m_upsampler[i].setup(oversampleStages, m_sampleRate); + m_downsampler[i].setup(oversampleStages, m_sampleRate); + } + + m_lp.setSampleRate(sampleRateOver); + m_lp.setLowpass(m_slewdistortionControls.m_splitModel.value()); + m_lp.clearHistory(); + + m_hp.setSampleRate(sampleRateOver); + m_hp.setHighpass(m_slewdistortionControls.m_splitModel.value()); + m_hp.clearHistory(); + + m_coeffPrecalc = -1.f / (sampleRateOver * 0.001f); + + m_dcCoeff = std::exp(-2.f * std::numbers::pi_v * SLEW_DISTORTION_DC_FREQ / sampleRateOver); + + std::fill(std::begin(m_inPeakDisplay), std::end(m_inPeakDisplay), 0.0f); + std::fill(std::begin(m_slewOut), std::end(m_slewOut), 0.0f); + std::fill(std::begin(m_dcOffset), std::end(m_dcOffset), 0.0f); + std::fill(std::begin(m_inEnv), std::end(m_inEnv), 0.0f); + std::fill(std::begin(m_outEnv), std::end(m_outEnv), 0.0f); + std::fill(std::begin(m_outPeakDisplay), std::end(m_outPeakDisplay), 0.0f); + for (auto& subArray : m_overOuts) {std::fill(subArray.begin(), subArray.end(), 0.0f);} + + m_biasInterpCoef = std::exp(-1 / (0.01f * m_sampleRate)); +} + + +extern "C" +{ +// necessary for getting instance out of shared lib +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* parent, void* data) +{ + return new SlewDistortion(parent, static_cast(data)); +} +} + +} // namespace lmms diff --git a/plugins/SlewDistortion/SlewDistortion.h b/plugins/SlewDistortion/SlewDistortion.h new file mode 100755 index 000000000..32dc094d4 --- /dev/null +++ b/plugins/SlewDistortion/SlewDistortion.h @@ -0,0 +1,91 @@ +/* + * SlewDistortion.h + * + * Copyright (c) 2025 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_SLEW_DISTORTION_H +#define LMMS_SLEW_DISTORTION_H + +#include "Effect.h" +#include "SlewDistortionControls.h" + +#include "BasicFilters.h" +#include "lmms_math.h" +#include "OversamplingHelpers.h" + +namespace lmms +{ +constexpr inline float SLEW_DISTORTION_MIN_FLOOR = 0.0012589f;// -72 dBFS +constexpr inline float SLEW_DISTORTION_DC_FREQ = 7.f; + +class SlewDistortion : public Effect +{ + Q_OBJECT +public: + SlewDistortion(Model* parent, const Descriptor::SubPluginFeatures::Key* key); + ~SlewDistortion() override = default; + ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override; + + EffectControls* controls() override + { + return &m_slewdistortionControls; + } + + float msToCoeff(float ms) + { + return (ms == 0) ? 0 : std::exp(m_coeffPrecalc / ms); + } +private slots: + void changeSampleRate(); +private: + alignas(16) std::array m_inPeakDisplay = {0}; + alignas(16) std::array m_slewOut = {0}; + alignas(16) std::array m_dcOffset = {0}; + alignas(16) std::array m_inEnv = {0}; + alignas(16) std::array m_outEnv = {0}; + alignas(16) std::array m_outPeakDisplay = {0}; + alignas(16) std::array, 2> m_overOuts = {{0}}; + + float m_sampleRate = 44100.f; + + int m_oldOversampleVal = -1; + float m_coeffPrecalc = 0; + float m_dcCoeff = 0; + float m_biasInterpCoef = 0; + float m_trueBias1 = 0; + float m_trueBias2 = 0; + + std::array, 2> m_upsampler; + std::array, 2> m_downsampler; + + StereoLinkwitzRiley m_lp; + StereoLinkwitzRiley m_hp; + + SlewDistortionControls m_slewdistortionControls; + + friend class SlewDistortionControls; + friend class gui::SlewDistortionControlDialog; +}; + +} // namespace lmms + +#endif // LMMS_SLEW_DISTORTION_H diff --git a/plugins/SlewDistortion/SlewDistortionControlDialog.cpp b/plugins/SlewDistortion/SlewDistortionControlDialog.cpp new file mode 100755 index 000000000..c57eaff6d --- /dev/null +++ b/plugins/SlewDistortion/SlewDistortionControlDialog.cpp @@ -0,0 +1,494 @@ +/* + * SlewDistortionControlDialog.cpp + * + * Copyright (c) 2025 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "SlewDistortionControlDialog.h" +#include "SlewDistortionControls.h" +#include "SlewDistortion.h" + +#include "embed.h" +#include "Knob.h" +#include "MainWindow.h" +#include +#include "GuiApplication.h" +#include "PixmapButton.h" +#include "Draggable.h" +#include "lmms_math.h" + +#include + +namespace lmms::gui +{ + +SlewDistortionControlDialog::SlewDistortionControlDialog(SlewDistortionControls* controls) : + EffectControlDialog(controls), + m_controls(controls) +{ + using DirectionOfManipulation = FloatModelEditorBase::DirectionOfManipulation; + + setAutoFillBackground(true); + QPalette pal; + pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); + setPalette(pal); + setFixedSize(638, 271); + + auto makeKnob = [this](int x, int y, const QString& hintText, const QString& unit, FloatModel* model, bool smol = false) + { + Knob* newKnob = new Knob(smol ? KnobType::Small17 : KnobType::Bright26, this); + newKnob->move(x, y); + newKnob->setModel(model); + newKnob->setHintText(hintText, unit); + return newKnob; + }; + + auto makeToggleButton = [this](int x, int y, const QString& tooltip, const std::string& activeIcon, const std::string& inactiveIcon, BoolModel* model) + { + PixmapButton* button = new PixmapButton(this, tooltip); + button->setActiveGraphic(PLUGIN_NAME::getIconPixmap(activeIcon)); + button->setInactiveGraphic(PLUGIN_NAME::getIconPixmap(inactiveIcon)); + button->setToolTip(tooltip); + button->move(x, y); + button->setCheckable(true); + button->setModel(model); + return button; + }; + + auto makeGroupButton = [this](int x, int y, const QString& tooltip, const std::string& activeIcon, const std::string& inactiveIcon) + { + PixmapButton* button = new PixmapButton(this, tooltip); + button->setActiveGraphic(PLUGIN_NAME::getIconPixmap(activeIcon)); + button->setInactiveGraphic(PLUGIN_NAME::getIconPixmap(inactiveIcon)); + button->setToolTip(tooltip); + button->move(x, y); + return button; + }; + + ComboBox* distType1Box = new ComboBox(this); + distType1Box->setGeometry(85, 26, 115, 22); + //distType1Box->setFont(pointSize<8>(distType1Box->font())); + distType1Box->setModel(&controls->m_distType1Model); + + ComboBox* distType2Box = new ComboBox(this); + distType2Box->setGeometry(85, 147, 115, 22); + //distType2Box->setFont(pointSize<8>(distType2Box->font())); + distType2Box->setModel(&controls->m_distType2Model); + + Draggable* drive1Draggable = new Draggable(DirectionOfManipulation::Vertical, + &controls->m_drive1Model, PLUGIN_NAME::getIconPixmap("handle"), 108, 34, this); + drive1Draggable->move(16, drive1Draggable->y()); + drive1Draggable->setDefaultValPixmap(PLUGIN_NAME::getIconPixmap("handle_zero")); + Draggable* drive2Draggable = new Draggable(DirectionOfManipulation::Vertical, + &controls->m_drive2Model, PLUGIN_NAME::getIconPixmap("handle"), 229, 155, this); + drive2Draggable->move(16, drive2Draggable->y()); + drive2Draggable->setDefaultValPixmap(PLUGIN_NAME::getIconPixmap("handle_zero")); + + Draggable* bias1Draggable = new Draggable(DirectionOfManipulation::Vertical, + &controls->m_bias1Model, PLUGIN_NAME::getIconPixmap("handle"), 112, 34, this); + bias1Draggable->move(416, bias1Draggable->y()); + bias1Draggable->setDefaultValPixmap(PLUGIN_NAME::getIconPixmap("handle_zero")); + Draggable* bias2Draggable = new Draggable(DirectionOfManipulation::Vertical, + &controls->m_bias2Model, PLUGIN_NAME::getIconPixmap("handle"), 233, 155, this); + bias2Draggable->move(416, bias2Draggable->y()); + bias2Draggable->setDefaultValPixmap(PLUGIN_NAME::getIconPixmap("handle_zero")); + + m_slewUp1Knob = makeKnob(96, 65, tr("Slew Up 1:"), "", &controls->m_slewUp1Model); + m_slewUp2Knob = makeKnob(96, 186, tr("Slew Up 2:"), "", &controls->m_slewUp2Model); + m_slewDown1Knob = makeKnob(163, 65, tr("Slew Down 1:"), "", + controls->m_slewLink1Model.value() ? &controls->m_slewUp1Model : &controls->m_slewDown1Model); + m_slewDown2Knob = makeKnob(163, 186, tr("Slew Down 2:"), "", + controls->m_slewLink2Model.value() ? &controls->m_slewUp2Model : &controls->m_slewDown2Model); + makeKnob(329, 26, tr("Warp 1:"), "", &controls->m_warp1Model); + makeKnob(329, 147, tr("Warp 2:"), "", &controls->m_warp2Model); + makeKnob(371, 26, tr("Crush 1:"), "", &controls->m_crush1Model); + makeKnob(371, 147, tr("Crush 2:"), "", &controls->m_crush2Model); + makeKnob(225, 65, tr("Attack 1:"), "", &controls->m_attack1Model); + makeKnob(225, 186, tr("Attack 2:"), "", &controls->m_attack2Model); + makeKnob(267, 65, tr("Release 1:"), "", &controls->m_release1Model); + makeKnob(267, 186, tr("Release 2:"), "", &controls->m_release2Model); + makeKnob(225, 26, tr("Dynamics 1:"), "", &controls->m_dynamics1Model); + makeKnob(225, 147, tr("Dynamics 2:"), "", &controls->m_dynamics2Model); + makeKnob(267, 26, tr("Dynamic Slew 1:"), "", &controls->m_dynamicSlew1Model); + makeKnob(267, 147, tr("Dynamic Slew 2:"), "", &controls->m_dynamicSlew2Model); + + Draggable* outVol1Draggable = new Draggable(DirectionOfManipulation::Vertical, + &controls->m_outVol1Model, PLUGIN_NAME::getIconPixmap("handle"), 108, 34, this); + outVol1Draggable->move(594, outVol1Draggable->y()); + outVol1Draggable->setDefaultValPixmap(PLUGIN_NAME::getIconPixmap("handle_zero")); + Draggable* outVol2Draggable = new Draggable(DirectionOfManipulation::Vertical, + &controls->m_outVol2Model, PLUGIN_NAME::getIconPixmap("handle"), 229, 155, this); + outVol2Draggable->move(594, outVol2Draggable->y()); + outVol2Draggable->setDefaultValPixmap(PLUGIN_NAME::getIconPixmap("handle_zero")); + + makeToggleButton(132, 70, tr("Slew Link 1"), "link_on", "link_off", &controls->m_slewLink1Model); + connect(&controls->m_slewLink1Model, &BoolModel::dataChanged, this, [this, controls]{ + if (controls->m_slewLink1Model.value()) + { + controls->m_slewDown1Model.setValue(controls->m_slewUp1Model.value()); + m_slewDown1Knob->setModel(&controls->m_slewUp1Model); + } + else + { + m_slewDown1Knob->setModel(&controls->m_slewDown1Model); + } + }); + makeToggleButton(132, 191, tr("Slew Link 2"), "link_on", "link_off", &controls->m_slewLink2Model); + connect(&controls->m_slewLink2Model, &BoolModel::dataChanged, this, [this, controls]{ + if (controls->m_slewLink2Model.value()) + { + controls->m_slewDown2Model.setValue(controls->m_slewUp2Model.value()); + m_slewDown2Knob->setModel(&controls->m_slewUp2Model); + } + else + { + m_slewDown2Knob->setModel(&controls->m_slewDown2Model); + } + }); + + makeToggleButton(9, 248, tr("DC Offset Removal"), "dc_on", "dc_off", &controls->m_dcRemoveModel); + makeToggleButton(99, 248, tr("Multiband"), "mb_on", "mb_off", &controls->m_multibandModel); + + makeKnob(190, 249, tr("Split:"), "", &controls->m_splitModel, true); + makeKnob(338, 78, tr("Mix 1:"), "", &controls->m_mix1Model); + makeKnob(338, 199, tr("Mix 2:"), "", &controls->m_mix2Model); + + PixmapButton* oversample1xButton = makeGroupButton(454, 248, tr("Disable Oversampling"), "oversample_1x_on", "oversample_1x_off"); + PixmapButton* oversample2xButton = makeGroupButton(479, 248, tr("2x Oversampling"), "oversample_2x_on", "oversample_2x_off"); + PixmapButton* oversample4xButton = makeGroupButton(504, 248, tr("4x Oversampling"), "oversample_4x_on", "oversample_4x_off"); + PixmapButton* oversample8xButton = makeGroupButton(529, 248, tr("8x Oversampling"), "oversample_8x_on", "oversample_8x_off"); + PixmapButton* oversample16xButton = makeGroupButton(554, 248, tr("16x Oversampling"), "oversample_16x_on", "oversample_16x_off"); + PixmapButton* oversample32xButton = makeGroupButton(579, 248, tr("32x Oversampling"), "oversample_32x_on", "oversample_32x_off"); + + AutomatableButtonGroup* oversampleGroup = new AutomatableButtonGroup(this); + oversampleGroup->addButton(oversample1xButton); + oversampleGroup->addButton(oversample2xButton); + oversampleGroup->addButton(oversample4xButton); + oversampleGroup->addButton(oversample8xButton); + oversampleGroup->addButton(oversample16xButton); + oversampleGroup->addButton(oversample32xButton); + oversampleGroup->setModel(&controls->m_oversamplingModel); + + PixmapButton* m_helpBtn = new PixmapButton(this, nullptr); + m_helpBtn->move(614, 250); + m_helpBtn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("help_on")); + m_helpBtn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("help_off")); + m_helpBtn->setToolTip(tr("Open help window")); + connect(m_helpBtn, &PixmapButton::clicked, this, &SlewDistortionControlDialog::showHelpWindow); + + connect(getGUI()->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(update())); +} + +void SlewDistortionControlDialog::paintEvent(QPaintEvent* event) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + QRect inMeters[] = { {22, 31, 8, 75}, {30, 31, 8, 75}, {22, 152, 8, 75}, {30, 152, 8, 75} }; + QRect outMeters[] = { {600, 31, 8, 75}, {608, 31, 8, 75}, {600, 152, 8, 75}, {608, 152, 8, 75} }; + + float* inPeak = &m_controls->m_effect->m_inPeakDisplay[0]; + float* outPeak = &m_controls->m_effect->m_outPeakDisplay[0]; + + for (int i = 0; i < 4; ++i) + { + m_lastInPeaks[i] = std::max((inPeak[i] != -1.0f) ? inPeak[i] : m_lastInPeaks[i], SLEW_DISTORTION_MIN_FLOOR); + m_lastOutPeaks[i] = std::max((outPeak[i] != -1.0f) ? outPeak[i] : m_lastOutPeaks[i], SLEW_DISTORTION_MIN_FLOOR); + inPeak[i] = outPeak[i] = -1.0f; + } + + auto drawInverseMeters = [&p](const QRect meters[], const float values[], QColor coverColor) + { + const float dbfsMin = -24.0f; + const float dbfsMax = 24.0f; + + for (int i = 0; i < 4; ++i) + { + float valueDbfs = ampToDbfs(values[i]); + + float normalizedValue = (valueDbfs - dbfsMin) / (dbfsMax - dbfsMin); + normalizedValue = std::clamp(normalizedValue, 0.0f, 1.0f); + + int coveredHeight = static_cast(meters[i].height() * (1.0f - normalizedValue)); + QRect coveredRect(meters[i].left(), meters[i].top(), meters[i].width(), coveredHeight); + + p.fillRect(coveredRect, coverColor); + } + }; + + drawInverseMeters(inMeters, &m_lastInPeaks[0], QColor(10, 10, 10)); + drawInverseMeters(outMeters, &m_lastOutPeaks[0], QColor(10, 10, 10)); + + QRect curveRect1(452, 10, 100, 100); + QRect curveRect2(452, 131, 100, 100); + + QPen gridPen(QColor(36, 40, 48)); + gridPen.setStyle(Qt::DotLine); + p.setPen(gridPen); + + auto drawGrid = [&p](const QRect& rect) + { + for (int i = 1; i < 8; ++i) + { + int x = rect.left() + i * rect.width() / 8 + 1; + p.drawLine(x, rect.top() + 1, x, rect.bottom()); + + int y = rect.top() + i * rect.height() / 8 + 1; + p.drawLine(rect.left() + 1, y, rect.right(), y); + } + }; + + drawGrid(curveRect1); + drawGrid(curveRect2); + + QPen axisPen(QColor(62, 66, 75)); + axisPen.setWidth(2); + p.setPen(axisPen); + + auto drawAxes = [&p](const QRect& rect) + { + p.drawLine(rect.center().x() + 2, rect.top() + 1, rect.center().x() + 2, rect.bottom()); + p.drawLine(rect.left() + 1, rect.center().y() + 2, rect.right(), rect.center().y() + 2); + }; + + drawAxes(curveRect1); + drawAxes(curveRect2); + + auto drawCurve = [&](const QRect& rect, int band) + { + QVector points; + + QPen curvePen(QColor(34, 226, 108)); + curvePen.setWidth(2); + p.setPen(curvePen); + + const int distType = band == 0 ? m_controls->m_distType1Model.value() : m_controls->m_distType2Model.value(); + const float drive = dbfsToAmp(band == 0 ? m_controls->m_drive1Model.value() : m_controls->m_drive2Model.value()); + const float bias = band == 0 ? m_controls->m_bias1Model.value() : m_controls->m_bias2Model.value(); + const float warp = band == 0 ? m_controls->m_warp1Model.value() : m_controls->m_warp2Model.value(); + const float crush = dbfsToAmp(band == 0 ? m_controls->m_crush1Model.value() : m_controls->m_crush2Model.value()); + + const float halfLineWidth = curvePen.widthF() / 2.0f; + const float amplitudeScale = (rect.height() - curvePen.widthF()) / rect.height(); + + const int numSteps = curveRect1.width() * 2; + for (int i = 0; i <= numSteps; ++i) + { + float x = -1.0f + 2.0f * i / numSteps; + + float biasedIn = x * drive + bias; + float distIn = (biasedIn - copysign(warp / crush, biasedIn)) / (1.0f - warp); + float distOut; + + switch (static_cast(distType)) + { + case SlewDistortionType::HardClip: { + distOut = std::clamp(distIn, -1.f, 1.f); + break; + } + case SlewDistortionType::Tanh: { + const float temp = std::clamp(distIn, -40.f, 40.f); + distOut = 2.f / (1.f + std::exp(-2.f * temp)) - 1; + break; + } + case SlewDistortionType::FastSoftClip1: { + const float temp = std::clamp(distIn, -2.f, 2.f); + distOut = temp / (1 + 0.25f * temp * temp); + break; + } + case SlewDistortionType::FastSoftClip2: { + const float temp = std::clamp(distIn, -1.5f, 1.5f); + distOut = temp - (4.f / 27.f) * temp * temp * temp; + break; + } + case SlewDistortionType::Sinusoidal: { + // using a polynomial approximation so it matches with the SSE2 code + // x - x^3 / 6 + x^5 / 120 + float modInput = std::fmod(distIn - std::numbers::pi_v * 0.5f, 2.f * std::numbers::pi_v); + if (modInput < 0) {modInput += 2.f * std::numbers::pi_v;} + const float x = std::abs(modInput - std::numbers::pi_v) - std::numbers::pi_v * 0.5f; + const float x2 = x * x; + const float x3 = x2 * x; + const float x5 = x3 * x2; + distOut = x - (x3 / 6.0f) + (x5 / 120.0f); + break; + } + case SlewDistortionType::Foldback: { + distOut = std::abs(std::abs(std::fmod(distIn - 1.f, 4.f)) - 2.f) - 1.f; + break; + } + case SlewDistortionType::FullRectify: { + distOut = std::abs(distIn); + break; + } + case SlewDistortionType::SmoothRectify: + { + distOut = std::sqrt(distIn * distIn + 0.04f) - 0.2f; + break; + } + case SlewDistortionType::HalfRectify: + { + distOut = std::max(0.0f, distIn); + break; + } + case SlewDistortionType::Bitcrush: + { + const float scale = 16 / drive; + distOut = std::round(distIn / drive * scale) / scale; + break; + } + default: + { + distOut = distIn; + } + } + + distOut = distOut * (1.0f - warp) + copysign(warp, biasedIn); + if (std::abs(biasedIn) < warp / crush) + { + distOut = biasedIn * crush; + } + + distOut *= amplitudeScale; + + float px = rect.left() + (x + 1.f) * 0.5f * rect.width(); + float py = rect.bottom() - (distOut + 1.f) * 0.5f * rect.height(); + + py += halfLineWidth; + + points.append(QPointF(px, py)); + } + + QPainterPath path; + path.addPolygon(QPolygonF(points)); + p.save(); + p.setClipRect(rect); + p.drawPath(path); + p.restore(); + }; + + drawCurve(curveRect1, 0); + drawCurve(curveRect2, 1); +} + +void SlewDistortionControlDialog::showHelpWindow() +{ + SlewDistortionHelpView::getInstance()->close(); + SlewDistortionHelpView::getInstance()->show(); +} + + +QString SlewDistortionHelpView::s_helpText = tr( +"
" +"Slew Distortion

" +"Plugin by Lost Robot
" +"GUI by thismoon
" +"
" +"

Overview:

" +"Slew Distortion is a multiband slew rate limiter and distortion effect.

" +"Slew rate limiting is something I accidentally invented while trying to make a lowpass filter for the first time.
" +"In short, a slew rate limiter limits how quickly the waveform can move from one point to the next.
" +"You'll hear that it has a similar quality to a lowpass filter, in that it does quieten the high frequencies by quite a bit.
" +"However, the intensity of this effect depends heavily on the input signal, and with it comes a rather unique distortion of that signal.

" +"In this plugin, the slew rate limiting is followed by waveshaping distortion.
" +"Every distortion type is a pure waveshaping function with no filters or delays of any kind involved.
" +"These distortions will generate new harmonics at exact frequency multiples of the incoming audio.

" +"Because the plugin is multiband, you can apply these effects to different frequency ranges independently.
" +"

Distortion Types:

" +"Hard Clip - Aggressively clamps the audio signal to 0 dBFS.
" +"This leaves the signal entirely untouched until it passes the clamping threshold, beyond which all content is clipped out entirely.
" +"Tanh - A very gentle sigmoid distortion.
" +"This waveshape is mathematically smooth and continuous at all derivatives.
" +"It can be pushed significantly harder than most other distortion shapes before it starts generating harsh high frequencies.
" +"Fast Soft Clip 1 - A CPU-efficient soft clipping function.
" +"Fast Soft Clip 2 - A CPU-efficient cubic soft clipping function.
" +"Sinusoidal - Incredibly smooth wavewrapping distortion.
" +"Unlike all the previous distortion types, loud audio information is not entirely lost or clipped away, and is instead wrapped back down to lower values.
" +"Foldover - A non-smooth wavewrapping alternative.
" +"This leaves the audio values untouched relative to neighboring values,
" +"except at the borders where the waveshape sharply changes directions, generating harsh distortion.
" +"Full-wave Rectify - Flips the bottom half of the waveform to the top half.
" +"The timbre of this commonly sounds similar to shifting the audio upward by one octave.
" +"Unlike all the previous distortion types, this one is asymmetrical by default, meaning it will generate even-multiple harmonics.
" +"Smooth Rectify - An alternative to Full-wave Rectify which has a smooth corner.
" +"Half-wave Rectify - An alternative to Full-wave Rectify which clips all negative audio samples instead of reflecting them upward.
" +"Bitcrush - Bit depth reduction. This distortion type is special-cased to have the Drive change its shape instead of its input amplitude.
" +"

Slew:

" +"This section controls the slew rate limit, the speed at which the incoming waveform's values can change.
" +"Up and Down control the slew rate limit for upward and downward movement, respectively.
" +"The Slew Link button locks the Slew Up and Slew Down parameters to the same value, for convenience.
" +"

Dynamics:

" +"This section uses an envelope follower to track the volume of the incoming audio signal.
" +"Amount - Restores the dynamic range lost from the distortion and slew rate limiting by matching the output volume to the input volume.
" +"Slew - Dynamically changes the slew rate, depending on the input volume.
" +"Attack - How quickly the envelope follower responds to increases in volume (e.g. transients).
" +"Release - How quickly the envelope follower responds to decreases in volume.
" +"

Shape:

" +"This section allows further sculpting of the distortion shape beyond what the distortion types can achieve on their own.
" +"Warp - Causes input values smaller than this value to be unimpacted by the waveshaping.
" +"The distortion shape is properly scaled and shifted to ensure it remains perfectly clean and continuous.
" +"Crush - Increases the volume of audio below the Warp value.
" +"This adds a sharp corner to the waveshaping function, resulting in much more aggressive distortion.
" +"

Miscellaneous:

" +"Mix - Blends between the wet and dry signals for the current band.
" +"Since both the wet and dry signal are after the crossover filter and have oversampling applied,
" +"this parameter is entirely immune to phase issues caused by blending signals.
" +"Bias - Adds DC offset to the input signal before the distortion, causing the waveshaping to be asymmetrical.
" +"This allows every distortion type to generate even-multiple harmonics, including the symmetrical types which usually only generate odd-multiple harmonics.
" +"DC Remover - Removes DC offset (0 Hz audio) from the output signal. You'll almost always want to leave this enabled.
" +"Multiband - Splits the signal into two frequency bands. If disabled, the top band's parameters are applied to the entire audio signal.
" +"Split - The crossover frequency at which the Multiband mode splits the signal into two bands.
" +"

Oversampling:

" +"An audio signal is only capable of storing frequencies below Nyquist, which is half of the sample rate.
" +"If any form of distortion generates new frequencies that are above this Nyquist frequency, they will be reflected (aliased) back downward.
" +"For example, if the distortion generates a harmonic that is 5000 Hz above Nyquist, that frequency will be aliased down to 5000 Hz below Nyquist.
" +"This aliasing is inharmonic, oftentimes sounds unpleasant, and can even contribute to auditory masking within the song.

" +"Oversampling helps to resolve this issue by temporarily increasing the sample rate of the signal,
" +"so significantly higher frequencies can be supported before they start aliasing back into the audible range.
" +"Those higher frequencies are then filtered out before decreasing the sample rate back to its original value so they don't alias.

" +"This plugin supports up to five stages of oversampling.
" +"Each stage provides an extra 2 octaves of headroom before frequencies alias far enough to become audible.
" +"The number on the button is how much the sample rate is increased by. THE PLUGIN'S CPU USAGE WILL BE INCREASED BY APPROXIMATELY THE SAME AMOUNT.
" +"Even just 2x oversampling can make a massive difference and is oftentimes all you need, but up to 32x oversampling is supported.
" +); + + + +SlewDistortionHelpView::SlewDistortionHelpView() : QTextEdit(s_helpText) +{ +#if (QT_VERSION < QT_VERSION_CHECK(5,12,0)) + // Bug workaround: https://codereview.qt-project.org/c/qt/qtbase/+/225348 + using ::operator|; +#endif + setWindowTitle("Slew Distortion Help"); + setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse); + getGUI()->mainWindow()->addWindowedWidget(this); + parentWidget()->setAttribute(Qt::WA_DeleteOnClose, false); + parentWidget()->setWindowIcon(PLUGIN_NAME::getIconPixmap("logo")); + + // No maximize button + Qt::WindowFlags flags = parentWidget()->windowFlags(); + flags &= ~Qt::WindowMaximizeButtonHint; + parentWidget()->setWindowFlags(flags); +} + + +} // namespace lmms::gui diff --git a/plugins/SlewDistortion/SlewDistortionControlDialog.h b/plugins/SlewDistortion/SlewDistortionControlDialog.h new file mode 100755 index 000000000..c7aec9179 --- /dev/null +++ b/plugins/SlewDistortion/SlewDistortionControlDialog.h @@ -0,0 +1,84 @@ +/* + * SlewDistortionControlDialog.h + * + * Copyright (c) 2025 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GUI_SLEW_DISTORTION_CONTROL_DIALOG_H +#define LMMS_GUI_SLEW_DISTORTION_CONTROL_DIALOG_H + +#include "EffectControlDialog.h" +#include +#include + +namespace lmms +{ + +class SlewDistortionControls; +class FloatModel; + +namespace gui +{ + +class Knob; + +class SlewDistortionControlDialog : public EffectControlDialog +{ + Q_OBJECT +public: + SlewDistortionControlDialog(SlewDistortionControls* controls); + ~SlewDistortionControlDialog() override = default; + + void paintEvent(QPaintEvent* event) override; +public slots: + void showHelpWindow(); +private: + SlewDistortionControls* m_controls; + + Knob* m_slewUp1Knob; + Knob* m_slewUp2Knob; + Knob* m_slewDown1Knob; + Knob* m_slewDown2Knob; + + std::array m_lastInPeaks = {0}; + std::array m_lastOutPeaks = {0}; +}; + +class SlewDistortionHelpView : public QTextEdit +{ + Q_OBJECT +public: + static SlewDistortionHelpView* getInstance() + { + static SlewDistortionHelpView instance; + return &instance; + } + +private: + SlewDistortionHelpView(); + static QString s_helpText; +}; + +} // namespace gui + +} // namespace lmms + +#endif // LMMS_GUI_SLEW_DISTORTION_CONTROL_DIALOG_H diff --git a/plugins/SlewDistortion/SlewDistortionControls.cpp b/plugins/SlewDistortion/SlewDistortionControls.cpp new file mode 100755 index 000000000..9f233c759 --- /dev/null +++ b/plugins/SlewDistortion/SlewDistortionControls.cpp @@ -0,0 +1,184 @@ +/* + * SlewDistortionControls.cpp + * + * Copyright (c) 2025 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "SlewDistortionControls.h" + +#include + +#include "SlewDistortion.h" + +namespace lmms +{ + +SlewDistortionControls::SlewDistortionControls(SlewDistortion* effect) : + EffectControls(effect), + m_effect(effect), + m_distType1Model(this, tr("Type 1")), + m_distType2Model(this, tr("Type 2")), + m_drive1Model(0.0f, -24.f, 24.0f, 0.0001f, this, tr("Drive 1")), + m_drive2Model(0.0f, -24.f, 24.0f, 0.0001f, this, tr("Drive 2")), + m_slewUp1Model(6.0f, -96.f, 6.0f, 0.0001f, this, tr("Slew Up 1")), + m_slewUp2Model(6.0f, -96.f, 6.0f, 0.0001f, this, tr("Slew Up 2")), + m_slewDown1Model(6.0f, -96.f, 6.0f, 0.0001f, this, tr("Slew Down 1")), + m_slewDown2Model(6.0f, -96.f, 6.0f, 0.0001f, this, tr("Slew Down 2")), + m_bias1Model(0.0f, -2.0f, 2.0f, 0.0001f, this, tr("Bias 1")), + m_bias2Model(0.0f, -2.0f, 2.0f, 0.0001f, this, tr("Bias 2")), + m_warp1Model(0.0f, 0.0f, 0.99f, 0.0001f, this, tr("Warp 1")), + m_warp2Model(0.0f, 0.0f, 0.99f, 0.0001f, this, tr("Warp 2")), + m_crush1Model(0.0f, 0.0f, 24.0f, 0.0001f, this, tr("Crush 1")), + m_crush2Model(0.0f, 0.0f, 24.0f, 0.0001f, this, tr("Crush 2")), + m_outVol1Model(0.0f, -24.0f, 24.0f, 0.0001f, this, tr("Out Vol 1")), + m_outVol2Model(0.0f, -24.0f, 24.0f, 0.0001f, this, tr("Out Vol 2")), + m_attack1Model(2.0f, 0.01f, 200.0f, 0.01f, this, tr("Attack 1")), + m_attack2Model(2.0f, 0.01f, 200.0f, 0.01f, this, tr("Attack 2")), + m_release1Model(20.0f, 0.01f, 800.0f, 0.01f, this, tr("Release 1")), + m_release2Model(20.0f, 0.01f, 800.0f, 0.01f, this, tr("Release 2")), + m_dynamics1Model(0.0f, 0.0f, 1.0f, 0.0001f, this, tr("Dynamics 1")), + m_dynamics2Model(0.0f, 0.0f, 1.0f, 0.0001f, this, tr("Dynamics 2")), + m_dynamicSlew1Model(0.0f, -8.0f, 8.0f, 0.0001f, this, tr("Dynamic Slew 1")), + m_dynamicSlew2Model(0.0f, -8.0f, 8.0f, 0.0001f, this, tr("Dynamic Slew 2")), + m_dcRemoveModel(true, this, tr("DC Offset Remover")), + m_multibandModel(false, this, tr("Multiband")), + m_oversamplingModel(0, 0, SLEWDIST_MAX_OVERSAMPLE_STAGES, this, tr("Oversample")), + m_splitModel(200.0f, 100.0f, 20000.0f, 0.1f, this, tr("Split")), + m_mix1Model(1.0f, 0.0f, 1.0f, 0.0001f, this, tr("Mix 1")), + m_mix2Model(1.0f, 0.0f, 1.0f, 0.0001f, this, tr("Mix 2")), + m_slewLink1Model(true, this, tr("Slew Link 1")), + m_slewLink2Model(true, this, tr("Slew Link 2")) +{ + m_slewUp1Model.setScaleLogarithmic(true); + m_slewUp2Model.setScaleLogarithmic(true); + m_slewDown1Model.setScaleLogarithmic(true); + m_slewDown2Model.setScaleLogarithmic(true); + m_crush1Model.setScaleLogarithmic(true); + m_crush2Model.setScaleLogarithmic(true); + m_attack1Model.setScaleLogarithmic(true); + m_attack2Model.setScaleLogarithmic(true); + m_release1Model.setScaleLogarithmic(true); + m_release2Model.setScaleLogarithmic(true); + m_dynamicSlew1Model.setScaleLogarithmic(true); + m_dynamicSlew2Model.setScaleLogarithmic(true); + m_splitModel.setScaleLogarithmic(true); + + m_distType1Model.addItem(tr("Hard Clip")); + m_distType1Model.addItem(tr("Tanh")); + m_distType1Model.addItem(tr("Fast Soft Clip 1")); + m_distType1Model.addItem(tr("Fast Soft Clip 2")); + m_distType1Model.addItem(tr("Sinusoidal")); + m_distType1Model.addItem(tr("Foldback")); + m_distType1Model.addItem(tr("Full Rectify")); + m_distType1Model.addItem(tr("Half Rectify")); + m_distType1Model.addItem(tr("Smooth Rectify")); + m_distType1Model.addItem(tr("Bitcrush")); + + m_distType2Model.addItem(tr("Hard Clip")); + m_distType2Model.addItem(tr("Tanh")); + m_distType2Model.addItem(tr("Fast Soft Clip 1")); + m_distType2Model.addItem(tr("Fast Soft Clip 2")); + m_distType2Model.addItem(tr("Sinusoidal")); + m_distType2Model.addItem(tr("Foldback")); + m_distType2Model.addItem(tr("Full Rectify")); + m_distType2Model.addItem(tr("Half Rectify")); + m_distType2Model.addItem(tr("Smooth Rectify")); + m_distType2Model.addItem(tr("Bitcrush")); +} + + +void SlewDistortionControls::loadSettings(const QDomElement& parent) +{ + m_distType1Model.loadSettings(parent, "distType1"); + m_distType2Model.loadSettings(parent, "distType2"); + m_drive1Model.loadSettings(parent, "drive1"); + m_drive2Model.loadSettings(parent, "drive2"); + m_slewUp1Model.loadSettings(parent, "slewUp1"); + m_slewUp2Model.loadSettings(parent, "slewUp2"); + m_slewDown1Model.loadSettings(parent, "slewDown1"); + m_slewDown2Model.loadSettings(parent, "slewDown2"); + m_bias1Model.loadSettings(parent, "bias1"); + m_bias2Model.loadSettings(parent, "bias2"); + m_warp1Model.loadSettings(parent, "warp1"); + m_warp2Model.loadSettings(parent, "warp2"); + m_crush1Model.loadSettings(parent, "crush1"); + m_crush2Model.loadSettings(parent, "crush2"); + m_outVol1Model.loadSettings(parent, "outVol1"); + m_outVol2Model.loadSettings(parent, "outVol2"); + m_attack1Model.loadSettings(parent, "attack1"); + m_attack2Model.loadSettings(parent, "attack2"); + m_release1Model.loadSettings(parent, "release1"); + m_release2Model.loadSettings(parent, "release2"); + m_dynamics1Model.loadSettings(parent, "dynamics1"); + m_dynamics2Model.loadSettings(parent, "dynamics2"); + m_dynamicSlew1Model.loadSettings(parent, "dynamicSlew1"); + m_dynamicSlew2Model.loadSettings(parent, "dynamicSlew2"); + m_dcRemoveModel.loadSettings(parent, "dcRemove"); + m_multibandModel.loadSettings(parent, "multiband"); + m_oversamplingModel.loadSettings(parent, "oversampling"); + m_splitModel.loadSettings(parent, "split"); + m_mix1Model.loadSettings(parent, "mix1"); + m_mix2Model.loadSettings(parent, "mix2"); + m_slewLink1Model.loadSettings(parent, "slewLink1"); + m_slewLink2Model.loadSettings(parent, "slewLink2"); +} + + + +void SlewDistortionControls::saveSettings(QDomDocument& doc, QDomElement& parent) +{ + m_distType1Model.saveSettings(doc, parent, "distType1"); + m_distType2Model.saveSettings(doc, parent, "distType2"); + m_drive1Model.saveSettings(doc, parent, "drive1"); + m_drive2Model.saveSettings(doc, parent, "drive2"); + m_slewUp1Model.saveSettings(doc, parent, "slewUp1"); + m_slewUp2Model.saveSettings(doc, parent, "slewUp2"); + m_slewDown1Model.saveSettings(doc, parent, "slewDown1"); + m_slewDown2Model.saveSettings(doc, parent, "slewDown2"); + m_bias1Model.saveSettings(doc, parent, "bias1"); + m_bias2Model.saveSettings(doc, parent, "bias2"); + m_warp1Model.saveSettings(doc, parent, "warp1"); + m_warp2Model.saveSettings(doc, parent, "warp2"); + m_crush1Model.saveSettings(doc, parent, "crush1"); + m_crush2Model.saveSettings(doc, parent, "crush2"); + m_outVol1Model.saveSettings(doc, parent, "outVol1"); + m_outVol2Model.saveSettings(doc, parent, "outVol2"); + m_attack1Model.saveSettings(doc, parent, "attack1"); + m_attack2Model.saveSettings(doc, parent, "attack2"); + m_release1Model.saveSettings(doc, parent, "release1"); + m_release2Model.saveSettings(doc, parent, "release2"); + m_dynamics1Model.saveSettings(doc, parent, "dynamics1"); + m_dynamics2Model.saveSettings(doc, parent, "dynamics2"); + m_dynamicSlew1Model.saveSettings(doc, parent, "dynamicSlew1"); + m_dynamicSlew2Model.saveSettings(doc, parent, "dynamicSlew2"); + m_dcRemoveModel.saveSettings(doc, parent, "dcRemove"); + m_multibandModel.saveSettings(doc, parent, "multiband"); + m_oversamplingModel.saveSettings(doc, parent, "oversampling"); + m_splitModel.saveSettings(doc, parent, "split"); + m_mix1Model.saveSettings(doc, parent, "mix1"); + m_mix2Model.saveSettings(doc, parent, "mix2"); + m_slewLink1Model.saveSettings(doc, parent, "slewLink1"); + m_slewLink2Model.saveSettings(doc, parent, "slewLink2"); +} + + + +} // namespace lmms diff --git a/plugins/SlewDistortion/SlewDistortionControls.h b/plugins/SlewDistortion/SlewDistortionControls.h new file mode 100755 index 000000000..0556ca46e --- /dev/null +++ b/plugins/SlewDistortion/SlewDistortionControls.h @@ -0,0 +1,120 @@ +/* + * SlewDistortionControls.h + * + * Copyright (c) 2025 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_SLEW_DISTORTION_CONTROLS_H +#define LMMS_SLEW_DISTORTION_CONTROLS_H + +#include "EffectControls.h" +#include "SlewDistortionControlDialog.h" +#include "ComboBox.h" + +namespace lmms +{ + +constexpr int SLEWDIST_MAX_OVERSAMPLE_STAGES = 5; + +class SlewDistortion; + +namespace gui +{ +class SlewDistortionControlDialog; +} + +enum class SlewDistortionType : int +{ + HardClip = 0, + Tanh, + FastSoftClip1, + FastSoftClip2, + Sinusoidal, + Foldback, + FullRectify, + HalfRectify, + SmoothRectify, + Bitcrush, + Count +}; + +class SlewDistortionControls : public EffectControls +{ + Q_OBJECT +public: + SlewDistortionControls(SlewDistortion* effect); + ~SlewDistortionControls() override = default; + + void saveSettings(QDomDocument& doc, QDomElement& parent) override; + void loadSettings(const QDomElement& parent) override; + inline QString nodeName() const override + { + return "SlewDistortionControls"; + } + gui::EffectControlDialog* createView() override + { + return new gui::SlewDistortionControlDialog(this); + } + int controlCount() override { return 32; } + +private: + SlewDistortion* m_effect; + + ComboBoxModel m_distType1Model; + ComboBoxModel m_distType2Model; + FloatModel m_drive1Model; + FloatModel m_drive2Model; + FloatModel m_slewUp1Model; + FloatModel m_slewUp2Model; + FloatModel m_slewDown1Model; + FloatModel m_slewDown2Model; + FloatModel m_bias1Model; + FloatModel m_bias2Model; + FloatModel m_warp1Model; + FloatModel m_warp2Model; + FloatModel m_crush1Model; + FloatModel m_crush2Model; + FloatModel m_outVol1Model; + FloatModel m_outVol2Model; + FloatModel m_attack1Model; + FloatModel m_attack2Model; + FloatModel m_release1Model; + FloatModel m_release2Model; + FloatModel m_dynamics1Model; + FloatModel m_dynamics2Model; + FloatModel m_dynamicSlew1Model; + FloatModel m_dynamicSlew2Model; + BoolModel m_dcRemoveModel; + BoolModel m_multibandModel; + IntModel m_oversamplingModel; + FloatModel m_splitModel; + FloatModel m_mix1Model; + FloatModel m_mix2Model; + BoolModel m_slewLink1Model; + BoolModel m_slewLink2Model; + + friend class gui::SlewDistortionControlDialog; + friend class SlewDistortion; +}; + +} // namespace lmms + +#endif // LMMS_SLEW_DISTORTION_CONTROLS_H diff --git a/plugins/SlewDistortion/artwork.png b/plugins/SlewDistortion/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..8f516729c82107dc1f943103e7c7823ee004cc57 GIT binary patch literal 128479 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 z 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 L{hO6HY$N{3nPM}h~OU@NEH9jfF>xC@Wg)z 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 literal 0 HcmV?d00001 diff --git a/plugins/SlewDistortion/help_off.png b/plugins/SlewDistortion/help_off.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3b927f9da16624f756bcdc42da4d8b4996cb12 GIT binary patch literal 667 zcmV;M0%ZM(P)N; z2hJ;y`yiVjcR{!n3Wc0dr0jOP+r?t>tAM~sI-TYMfq;g77zF%6f}z9Vc!9q7>2#Ww zBA5J15=ahIzB12lx0}IWP;b0GaeaL?qE-v6v#uGJ~665PJ)UE2M}pNNNBdV%&cpkq?IhTP~MBA?YjZU>L{H$C7~8$utH?AHj@!=*vwCP}QI}!osW(gG=8Ri-o9Z+SN?yj)adP=@C58 zs5dJP6=cpi1L^Z%FyPRSV9B5Z@Ee%$lAI<2#Rx--fMTb%ZnvwJ%Vh}_?qUo&WyhUb zFd#RKiZL<-B(c})c}AnrJ8aL7i_M`;C=?0?oV>~Nyg+1_6ou3T^-E*v_xlW_*VStE z?4Mn|T=FBV*9m5oK!T*M>-?p8-_X~mMx!BF*uN$EJ0ZOAKotN0002ovPDHLkV1k30 BEaU(H literal 0 HcmV?d00001 diff --git a/plugins/SlewDistortion/help_on.png b/plugins/SlewDistortion/help_on.png new file mode 100644 index 0000000000000000000000000000000000000000..b9b51d6ed11c86061d613302675249c8c419055e GIT binary patch literal 595 zcmV-Z0<8UsP)ZA z;!qI%HfEm8xIswt31XH=NHjO-oFLpF;RI>lzW3C#KV>7>!1ctJTUz@Po(Wc?XNPR4Szr-0gO4 zfPX2WQ0On|`FuWcKA%b8BNz-?p!>x)5^3x8T8~DfZ}E6sq|<2?J-;ZCNaP_92nbo0 zoqmw$H|TxwJ)h4Vzk6si7Db*EJTy||0WE=Q0zk1?ECeR`4?Y7vlLI4F{&C{+&x~$X zQUtws^l5;inM@`NtaKN5I2=01>agL#Uiojy7<&Vh+`-)ThxaRb z;k|*bOaz!%G6rC9x!dj5`M9cub{$n8T^}!FCX?uqo=Dg-hi_DRXrp_>19TD^+AKtXV~ps5jv!F8b3 h23BAci$&Xw{RY=@81c>-ysQ8K002ovPDHLkV1hRd3BmvX literal 0 HcmV?d00001 diff --git a/plugins/SlewDistortion/link_off.png b/plugins/SlewDistortion/link_off.png new file mode 100644 index 0000000000000000000000000000000000000000..8b658abcbb2457126bfc1cea0abcba179e92e246 GIT binary patch literal 623 zcmV-#0+9WQP)}8zl=1DHbRjO;{k0@=OdH zkI6!2GBWO{8FT&4kE<~=X71Ge*16~2)BSxqopXM_9{{v7dtoN2WVghOqL*?*byjR_ z9MsUqhYJTgTMjLI&vH6NyuH0c4QVvd3m%d2Y~M6uSEX69>n%vaG$ z!S@eV%r7i*le1elZ{UiRgEIG5Sg;@nzY8f_e~F?i=(>=lkZUT-X0x-5mvD4)%D)p6 z6Ofsaj;re%D9%`hQkFfv0~i@I@oi=J$)2G7eLXz3yt1Y&V;M>fA=8%>bKoe0+wAr~ zP&pPBOmpfBCDC{l7UaS-G0e+B;AiIlg9ZibOLe^KzT;zIH5@b!^ClAX-fYFr?tZ}R zh6K|T1QT`t==cl|Rx6Y!pn0@S32yM2P0Dh}IDoFsHYim<#@=1(HWns^XrdNc*bq%fuuu!7 z^Ti{f5Ckn01l)xH!rpslJZI*zFTjL^yL)!;&dz-2pL6D%3ji0$Qe<|OY>1hHZ`PKr ztJGRU2ua36E4?hN^%jL8W@o0;nnoQ|N(CX81KaGKpclXMhCGzNO#~=LqQZSTr395y zf+nE)8aAsY2q?A?Wipurq;QOmI7x#{8AnFk`c@VT34(FqxWw zTqcx-7wtr!+vlLsiveVsJ22POj-ag@qPoKU2VIDg_|GL4LO^B)(g^NIq>&=(IvH`gM1 zIe@v=PGoMpbDq_iDxidtTvd+JJ13o(;7K0@f%7Hnf;g}WTI7}1Ab8r3@KLWjUaGMQ znVaunN2XoEN^YNULNeFh!hWA}=h^0wbwM0Rt*YPQq0b2Vum>U&!KCM*_isVzg9g~& zvk+U8E-VZ+Lz55zAx**Fb6{7>lv#D04nHL%qV4g)x5mJJ7{UDISI~{s5R^zP zqO<##3d3YedPy+iQnX3!C%NAy8zRBZx_-j+PY^7km~uJi;#$-`a>?^eggkeTxr$Px z4#@;Wl8zk5wCyw!*XtY<8B!UVqJpx^C}yt&4m$>+8mf@ES_gGxr#q+e@J?3{nOj}) z`CVM1wy1N(y<#Kv3gufT6@uKHggUzy^5`y7uVH$}5j^gL9iAk`i@^)M*l>rVUNM#W zr!k|E Tu}4N)00000NkvXXu0mjfpmwX1 literal 0 HcmV?d00001 diff --git a/plugins/SlewDistortion/logo.png b/plugins/SlewDistortion/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..9340da708dd79ed97111eb535f51b81a91d6a15b GIT binary patch literal 774 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 literal 0 HcmV?d00001 diff --git a/plugins/SlewDistortion/oversample_16x_off.png b/plugins/SlewDistortion/oversample_16x_off.png new file mode 100644 index 0000000000000000000000000000000000000000..ad85c1cdf4b9c9a2b0f44ac7281be34dd209d869 GIT binary patch literal 793 zcmV+!1LpjRP)y zR6!K~&AnIJkDhd0BZ9sl#I*i|2ntFtBoqYSdW=4>2&I(JqUY-#w zi2Ud+wTD7esNB8VTe~aE-aFH|bLZ~e>$)3c_=CC3oS8Yl^E)$hE(0L22{4?`p~FXE z7&Xvq3}~7zCzx8`MZr{`p>kyYoB-z>wq?Px3YbW|b%|C|DY?ure4>p)64@4Xjrodf z%O)N&Fb$eu*f=~zHiTY6#1sj0h?pW`>+VN99*1d~zO6LJDtCf9ju1}19G^v^0BAu= z^L|`7cN(AO<}q^jo-{Ya>(N8DXf%SY(J00f6L|FaNjae;6IdIq<^|)K9g^;@j-V4l zcULE-ra$6rPcP0;Yi~PFq)}9d2okZ9G|iHVzWyPk%yeK&OLG&RzkH27yLU=eo!zI9 z&1R*2bmUfX-NOfCXlSS}Q)H~qAB2-O2gBE|QUE3nkZLb=vvZ$iYj10n3da-gkV>U} zkD`=Fp!`tRAYLCsZ~q`>T-xdV3g?(=%B7woHUiar4>$a@idE28MzgRl3|WqQi5~ zPpCdM`3`K^|DDNTWhF_GnsS_>@z;e#M8e_X+Hfd@P-u&^DNjY8K9Q8j-A#(j;x?>8 zK{?Li`;VV!YTPHsvASC9*uD)(dg#u$s5ne(I!=aWIO^ zgI%zY&*$mk*>Zem>^`paU4)tbg?KE6XD?n6kx8U=I7k=b{H4o?M8dc+IDijRGgw+$ zmP)yLNAlM1+S};L(+zsJ8miTXSZytGtE;kAf~UkOL58kl;mf?M$cb903&OaDYDmhe z@VDFJrr+_)SxE`vHc3>IvK$3!bh(r2^F>Xv-f00MVM{WhX%#m93tS>y5mo!W{8#(| X5ky!-?r)kn00000NkvXXu0mjfceiWz literal 0 HcmV?d00001 diff --git a/plugins/SlewDistortion/oversample_16x_on.png b/plugins/SlewDistortion/oversample_16x_on.png new file mode 100644 index 0000000000000000000000000000000000000000..18c440cb7e729587fcb240224b46e17bda045ca2 GIT binary patch literal 910 zcmV;919AL`P))!Y35B`mb)i_s%`%4n+VMV=!X9wQg7h(^3Ex6Hsgb z`on507MFA zazMvb0^X*V7+CDYF3d+G$oW$-t^rY$1IQs1MgAo>UE|O?YzZ>qtYssDh z3dc4fJNMiJc-e+r@HH`h&Ltkk(+m%wIC8fK>HZtw&i>$~&`}x9vZ=6_J3;f>JnQnV zi{QQ#(W)6Jp4<#-FM=jDp?G==Xl`pz0ga3*;3`2`bT=6=WP>ddA!fFCmkI~hfV&4_ zmQI4b)`jfUW4MQ(VW0FO-myd+NMLWg1AiC*IW&q@M~H*onwB1%bDJj0SPFY6152Eo+BVI`x3l*GBUS^4qSIv2P5_%$X=-3e6C}-G`%3VOTy9T;yOr1z zg|mEjP-U#ENynPf(zo<1Z5yL~H`q1Ei`p|9o;eS+lLM)o)qu2>o3$OG0mcS@8~@VV kNmZ}`M9;458OD@00hEyaj+qq?@GbB$d&RH#{<0 zIpv;)=yJfhfEI>ORMfJl&n#QJBx4FvN(8X**0K~f>CWCtx`o3(mM5P8ka6!z}WqJvF+~Y2(Dcnz|`;QoXyF^mkdGXGWHD&nGl83 zMU{c)W}vsH$6RkoPTW!n)#?V`fB1;?YBhJh3<9bQ0fgX~|2GqYD*b~dKp_OZTTyt; zOWob3-PzskIHwhfE)~nsu1Kgd_ZV*@ef$ZFRf_di@nbSja5OKZ=DJ1KC+kUo8l7!DoPk zlnzvOx(LKNATzZ_cu*K}|LX#z>@D!dAfYmVRSUypCZsh!r$F-xVkt;1(jbBuYS2hK zVA-)8Zk_Yw7?jT-)NBt=CiY64TSD;QGQ^1%5XOK3MjLNH@41e`B#o?Ck^@V|JT!^N zvyb`+Hm)Ptdn>bco}}C2*tX-2-DkzhG7W=V_#?Tala#FU*DTvskbb!?+A#Wc=YvDm* zz2>UJ&!ID5mt-xdPSWmRziTrFnJ97AjB6WI@veC48R@M;vPcX!Rbg#spk2KaE~#s) yt^po&P5cEUokK257Gkf7T<)^=@SiTYS^5XG+Bob&gdm;(0000r^d2*gG0@Pnj+^pr$!2`m_aq-Hq8k=)nI4o={7aj+ZjANiQ2dHCpw0!aJ7 z(SE#)EGQPyY$Ht(S`>*4cK_Zj5$qir1}x93`m3hw!#75eDQ3I$840QebEAi&tFsd$ z*DmA5>$mt;nU4A772dRQ1A3h9nB7DQVum1U_<8z@#*sye4Ln!*&SyK_V3$^ zk(+mM;oM+ifg%<&v6u$QHgKm1i_=?Q4j=!w|Ma?iU-f4`41A2AU?oV z2^h>l)L_Jj#;_#r>^$14?wOsPHDfLz1wFG}(_LRZzN!@fG`2t-esrJS54o)XQFI|3 z35cB2?2>4EC(?RHYv_jtRfUC~K_go*)pF<5L{I11=g5WOX%i+2qNp55eDpX;a)!uS?)C-!`UD3@4NS-XbgB) zrr}+E0$W&^fi!un=YGMzKMVZ!41@HlZ!T&| zN9oZSsLCKjUkT2oVK^6uZ3!~w+ap@9lc?y?rxoarEB4?}H{`w^==o(Jh~VF@!ntr5 z^571r@xAaLyf@C}Np3`zVRVtQBg#D}+^isYI0yZifd5c`RF|N?)Zk8>fIs=%=nNw> z22#n8AtPMh0x_Uft%N(VAYhs;qZ3N-lC4^5G z4D|&QfR^A?9^D21)+@M|k3f{NWM~Ke54{DPvj?C?`k~8psIeh)BAWS%#@#n4Opc;4 zF$4X572%6TsPl&q-g}oOO#hQ0=)QBupdT-o$skRq#*Id4F`jbVk)2=g;#zQ@(Oas{doqWb8x!y>44suU1 g2-{BXZOI?sUw#iJO~GOoqyPW_07*qoM6N<$f_nj2AOHXW literal 0 HcmV?d00001 diff --git a/plugins/SlewDistortion/oversample_32x_off.png b/plugins/SlewDistortion/oversample_32x_off.png new file mode 100644 index 0000000000000000000000000000000000000000..c9ec66b40e30b3ed185abd1d237dc35f304e4dfd GIT binary patch literal 849 zcmV-X1FrmuP)sn0)`NT zX=YGHkCsGK)>G~3gO{E4A?N=)|3ClEjvFhLb>Qrr|37EWcW!eQ0Qi6AQQ|$CIIOR% zhGBT&@fy%|4+LqjiL0lkWX*LKPmYdrFveh6voOQc7<&0)!Vvf0fs-@@S~~Wn3U5}Q}ML_Id0#*FVFe;xo9R`Fcd;6={n9|#^=$m z_KVR%$VR!l=mLs1S@6-r`^oVG4849Mopp6yKzGk$n5K!kBL`65bP}P^7y|yK67*1A zJ${E&>0%cR&8H-YAmKc0MjTz|^_vnzko$gZMLF8rTI`BY0PU?O;P?AnX~vb#o$#)m zwJ0qq#v_%Dpy9V8j(%IsR=iRHiNM6Uw4?~tRU6PZFh~K7+a9?qqd5pl4hF}_@jFyi zu9GE1d=SNjvcypm{o1#8H?@bOw{IY3>>dX0G>AvuX`eM>SB^(ocLMpy3Xhe_p)s~6$(Whg_AtgL02nwpk!PIeY*x7VPp z{Vc*^)3Fh}gYIwLxrd3#No?7?Nq#@v6}TM6yp@GAMGvi^yqs*Bu7e1TjUzW_IiB?P z<6(D?H153AA)lse5Kf;thoaT1P*_xeE7!V^l9G(Jmg8uop_&ed;qmDB_8nfr)doVPwtGR1e`xQzo zR@!w;scayNfhncEGTb{_O8}Bc@8l%+oz7fS$G9cIxUMI7@o&fmVzWQQ;mdq~FaL@r bi0gg=e$`oXyBq6E!g|G*OqX2rdoLn7Gy;i8~XQy72=n zNce|OV8JH@0t5&H6Bt5ZW&-J+?tZW8a&A?>?rDC31(mMuSFb+jo^wy#1^{`={Sy0E zMh62EqpCfh@iAbdNx~RREbO!#8eY9 zq=YN`A+;QGs{z||JkJ9u=NEBq1?R5UfO)=e7BF6V)RPiigVI!#Kw*XUjI2h7ko`Co zqhFlB?Yx5GkCT9Nuxpkh+xQ;j<~#ut6fcRZrv^_qSD>Rs05)yOkagq2F2D31gIiiLYIvVL;kx!HvD|}GVHr6(E5Hq zq@Dx3m?ll)es$zCi`qW#HN5Rer!6(8Zut~;)ey{*EZX^{rOZ!tI+1(NupP57J~C z$GYpk+XMU7vhZBcoo?48s=v^w|D1!bw|X)oL!ZA5**S#@C9tFq-Vrn058Te-XX?QS zp*%4^hLHx-`nH|vMzOJ4AeXRA)2ws8L7;UbnEm@$WF&=a7eWVD=i&xsnVW|e15;i^ z%QZ}}wCxkr_OS|d#?n_!=aF%<=mM#h zdI!KdSJtM7wa2OLs)GJapqZnXsp+f9dyDSVXmjiv?gD`MpF!002ovPDHLk FV1kgo!-N0; literal 0 HcmV?d00001 diff --git a/plugins/SlewDistortion/oversample_4x_off.png b/plugins/SlewDistortion/oversample_4x_off.png new file mode 100644 index 0000000000000000000000000000000000000000..a9ad46909f1863247cdb34421a04de181de5b870 GIT binary patch literal 666 zcmV;L0%iS)P)IYqF`@I$T#X^6L>L4Jf(?9`{S<*}jfR=#`wrA?7*H6%6XYC)L8v$? zK>~sa-AC*w1bZnESqNzmSrFx}Z;$jD+p}Xe&_<-v(waI%?m`30KDQ>WpbyJEB1RQdOKc)yyv z5od}VNDD+7Y{$`a_s$LcSonoY7cb!4=qdE}9>AD-$Muz!Rg{WF)h}UYomEOS9mrUe z(Zm#N=H+YrT=#yv_4|A>E|*Fe9z2FQn;SoS1|L7qV)5U9Oh0>pt5+`L!K0~UzLd(P(v!B2Op6^n zbU3o-`KoUPy1g4>C;AHVJr9e2|3r=qoo#bVu_LMWE1?C&Y;+~1t{@pIi?&Efw|8n| z-lm>-GWVUxfH*WT76Rz{;2cbYJPX?D?`2P86IP5Fvl8!FegFUf07*qoM6N<$f^Iz` AI{*Lx literal 0 HcmV?d00001 diff --git a/plugins/SlewDistortion/oversample_4x_on.png b/plugins/SlewDistortion/oversample_4x_on.png new file mode 100644 index 0000000000000000000000000000000000000000..95ce8b54e0b020e1006de8fc46cf5e6d68c984ea GIT binary patch literal 768 zcmV+b1ONPqP)5 zBLP2A!C;b?w>#H+XLjH2`@|w-V0PZ_%$#%2nS19E0LB(HtB;}SF_^&$Xdnhz15l6E z8WOdQc`NT>2B*^36S&n4tSr0>L?k45@c__Ivc3 zp+c2{NN>(}6Uf=yfGvn9$_PB5>2ShSB4E4qNW7K+jZ}b&71TCr5T0D!u>r3IwQE*H zGP$FI`1&LU=4OFyLa4!*Or?UEtY@}U4q1xUqF>XERg37%L70)D0Iurd1RO;HDM_;g zB5*zR?_PPEPScmCVnBNb5zUMux&OX6A0e#VoI-SY0<^0NbM7$W8^=Jg>98|zrl)6W zg`A5$KMwcoE8Ox*0V3-gx1YnF9rH@ZrpthxA-0W`7iKeE!2vPkaD1XLx`qE(75#!_H+%T$zrKB*iy4) zhGvm!)<_1WkCuE!v7|WF#Ku#jNEbcVf%Neb+{L^(g9B=6g98g4})a z2zY+k-wdYR0t$u4aTIFGr>~5fVH}jRFG|f2_QlNE&A}{Zc5(#2?=1#J>^HOTdYt+7 zkt_7yTsY`FC(y{|^|6r&$kIQo)4Ptn`lfZh(1cyFeDvsafL)ut-5n~vY0v^l^`s!u ytK1wP4i}*9Tf_LDyIoas8b}m7-?t@yeE$F&A_k)KXd+Jl0000cL`Dio~>=9^9RAXLfe8$tI<=r5_~wcHX@A&G+WbECIl=3ed%0 zYui!idL6Vn9h7R)LDU3y6-3ztwO3a1aWKYUnI=s0ABG0sIYhs6IXO+&OGL|sB(h9s zl#~=%re*Wk1B8Ndg5|?iWPxiwBBDr$AR>y$#}|o2eM z-nog_Z{Es1tP0}mI__;A_u0VdQz!7KuOFH0XUA)|?D1nA_%xoC@1lj*tzCnusa#o{ z%A~~j6mcn$$;n)iiC;cnz@FW^y!Ti%iu~V#wBzv@4z@I-`|>rM>FPu%6!PYAk&CHZ z%pnrjz^x6@xN{6t&$|J?CMR+D&;hjUZ$d|V8{!*d=zlf8bS3$wF2Gw=J}&o>bOG}ggf{uw{D2eRZt6g&u5 z0%F8&rX&WQsb$Y-4IR_yL{NVNv|oO;K!QNf_(d-aBCsaLU`j!rsPrR}W1az#(gO8J zGpH1Ow!Emcb%Olze*x-bR&e`3!bt#lFc@rL!gYWRF2v-Rajt%M07?g9MUFB~2c9}5 z!cJV-2TFHjS%SgKKD--8A$M+uUJ9X`%Lwkh1=Hw)&3ccljJY7KJ7Cd99PK!h6MqYv;dABNaF z3FmkP;ge6)(h8$&WJH_>Fmv( zPO_G3hWr-??)iNPAJ0R*TS(4nxMz1G;JMKo4XTZ9byn41E_J!B%z7z+JTeXV#K)&r zpDIz!0%)p**uK%!D7e*tckuw^mSV0M27zT3As1|*5Iy?_r&@-8w~oU7({N^{5K)AP z6#P4NL=Qhge{PvtIv4g^aSzX&?f!{#Gte)7nwvoyr6$9drfpfUnna=hP>NPU>x6|! z_nD~+zq6h~Wk?vonnCl~Di)v4*&696wi{Xf$Xp+t7&@SD?QV~yCdb0c-blc_2fY#| z0#0rRCXsZa1?n=8GI9~dLB>sW8xHN + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Draggable.h" +#include "SimpleTextFloat.h" +#include "interpolation.h" + +#include +#include +#include + +namespace lmms::gui +{ + +Draggable::Draggable(DirectionOfManipulation directionOfManipulation, + FloatModel* floatModel, const QPixmap& pixmap, int pointA, int pointB, QWidget* parent) + : FloatModelEditorBase(directionOfManipulation, parent), + m_pixmap(pixmap), + m_defaultValPixmap(), + m_pointA(pointA), + m_pointB(pointB), + m_defaultValue(0), + m_hasDefaultValPixmap(false) +{ + setModel(floatModel); + connect(model(), &FloatModel::dataChanged, this, &Draggable::handleMovement); + handleMovement(); +} + +QSize Draggable::sizeHint() const +{ + return m_pixmap.size(); +} + +void Draggable::setPixmap(const QPixmap& pixmap) +{ + m_pixmap = pixmap; + update(); +} + +void Draggable::setDefaultValPixmap(const QPixmap& pixmap, float value) +{ + m_defaultValPixmap = pixmap; + m_defaultValue = value; + m_hasDefaultValPixmap = true; + update(); +} + +void Draggable::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event); + QPainter painter(this); + + if (m_hasDefaultValPixmap && model()->value() == m_defaultValue) + { + painter.drawPixmap(rect(), m_defaultValPixmap, m_defaultValPixmap.rect()); + } + else + { + painter.drawPixmap(rect(), m_pixmap, m_pixmap.rect()); + } +} + +void Draggable::mouseMoveEvent(QMouseEvent* me) +{ + QPoint pPos = mapToParent(me->pos()); + + if (m_buttonPressed && pPos != m_lastMousePos) + { + float point = (m_directionOfManipulation == DirectionOfManipulation::Vertical) ? pPos.y() : pPos.x(); + float progress = (point - m_pointA) / (m_pointB - m_pointA); + + if (progress >= 0 && progress <= 1) + { + float newVal = progress * (model()->maxValue() - model()->minValue()) + model()->minValue(); + model()->setValue(newVal); + } + else if (progress < 0) + { + model()->setValue(model()->minValue()); + } + else + { + model()->setValue(model()->maxValue()); + } + + emit sliderMoved(model()->value()); + m_lastMousePos = pPos; + s_textFloat->setText(displayValue()); + s_textFloat->moveGlobal(this, QPoint(width() + 2, 0)); + } +} + +void Draggable::handleMovement() +{ + float newCoord = std::lerp(m_pointA, m_pointB, (model()->value() - model()->minValue()) / (model()->maxValue() - model()->minValue())); + if (m_directionOfManipulation == DirectionOfManipulation::Vertical) + { + move(x(), newCoord - m_pixmap.height() / 2.f); + } + else if (m_directionOfManipulation == DirectionOfManipulation::Horizontal) + { + move(newCoord - m_pixmap.width() / 2.f, y()); + } +} + +} // namespace lmms::gui