From 8f4b1f0d613ec3e209d9276cf965e588b2b068eb Mon Sep 17 00:00:00 2001 From: MaximeSapountzis Date: Fri, 23 May 2025 10:44:06 +0200 Subject: [PATCH 1/5] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 95cbe98..c3678a8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ JUCE/ # Windows specific Thumbs.db +/.idea/vcs.xml From 4703c172a861848caf5580c03c58bbf95b14face Mon Sep 17 00:00:00 2001 From: MaximeSapountzis Date: Tue, 27 May 2025 10:28:33 +0200 Subject: [PATCH 2/5] Dsp distortion added --- include/DistortionEffect.h | 12 ++++++ src/effects/DistortionEffect.cpp | 63 ++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/include/DistortionEffect.h b/include/DistortionEffect.h index 5273391..c03764d 100644 --- a/include/DistortionEffect.h +++ b/include/DistortionEffect.h @@ -3,6 +3,7 @@ #include #include "AbstractEffect.h" + /** * @brief Represents a distortion effect that applies non-linear clipping to the audio stream. * Inherits from the AbstractEffect class. @@ -44,6 +45,9 @@ class DistortionEffect : public AbstractEffect { */ bool operator==(const AbstractEffect *effect) override; + + void prepare(const juce::dsp::ProcessSpec& spec); + /** * @brief Gets the type name of the effect for serialization purposes. * @return A string representing the effect type. @@ -86,4 +90,12 @@ class DistortionEffect : public AbstractEffect { * @brief The dry/wet mix of the effect. */ float mix; + + enum + { + waveshaperIndex + }; + + using ShaperType = juce::dsp::WaveShaper>; + juce::dsp::ProcessorChain processorChain; }; diff --git a/src/effects/DistortionEffect.cpp b/src/effects/DistortionEffect.cpp index 93d31cc..dd46110 100644 --- a/src/effects/DistortionEffect.cpp +++ b/src/effects/DistortionEffect.cpp @@ -5,11 +5,15 @@ /** * @brief Initializes a new instance of the DistortionEffect class. */ -DistortionEffect::DistortionEffect() { - // Default drive amount and mix +DistortionEffect::DistortionEffect() + : drive(1.0f), mix(1.0f) +{ effectName = "Distortion"; - drive = 1.0f; - mix = 0.5f; + auto& sh = processorChain.get(); + sh.functionToUse = [this](float x) + { + return std::tanh(drive * x); + }; } /** @@ -17,6 +21,15 @@ DistortionEffect::DistortionEffect() { */ DistortionEffect::~DistortionEffect() = default; + + +void DistortionEffect::prepare(const juce::dsp::ProcessSpec& spec) +{ + processorChain.prepare(spec); + processorChain.reset(); +} + + /** * @brief Applies the distortion effect to the given audio buffer. * @param bufferToFill The audio buffer to apply the effect to. @@ -29,30 +42,40 @@ DistortionEffect::~DistortionEffect() = default; * For a more advanced waveshaping approach, see: * https://docs.juce.com/master/structdsp_1_1WaveShaper.html */ -void DistortionEffect::apply(const juce::AudioSourceChannelInfo &bufferToFill) { - auto *leftBuffer = bufferToFill.buffer->getWritePointer(0, bufferToFill.startSample); - auto *rightBuffer = bufferToFill.buffer->getWritePointer(1, bufferToFill.startSample); - int numSamples = bufferToFill.numSamples; - - for (int i = 0; i < numSamples; ++i) { - const float inL = leftBuffer[i]; - const float inR = rightBuffer[i]; - // Apply drive and hard clip - const float wetL = juce::jlimit(-1.0f, 1.0f, inL * drive); - const float wetR = juce::jlimit(-1.0f, 1.0f, inR * drive); - - // Mix dry and wet signals - leftBuffer[i] = inL * (1.0f - mix) + wetL * mix; - rightBuffer[i] = inR * (1.0f - mix) + wetR * mix; +void DistortionEffect::apply (const juce::AudioSourceChannelInfo& bufferToFill) +{ + auto* buffer = bufferToFill.buffer; + const auto start = (size_t) bufferToFill.startSample; + const auto length = (size_t) bufferToFill.numSamples; + + auto fullBlock = juce::dsp::AudioBlock(*buffer, start); + + auto subBlock = fullBlock.getSubBlock (0, length); + + dsp::ProcessContextReplacing ctx (subBlock); + processorChain.process (ctx); + + for (int ch = 0; ch < buffer->getNumChannels(); ++ch) + { + auto* write = buffer->getWritePointer (ch, bufferToFill.startSample); + auto* read = buffer->getReadPointer (ch, bufferToFill.startSample); + for (int i = 0; i < bufferToFill.numSamples; ++i) + write[i] = read[i] * (1.0f - mix) + write[i] * mix; } } + /** * @brief Sets the drive amount for the distortion effect. * @param driveValue The drive (gain) amount before clipping. */ void DistortionEffect::setDrive(float driveValue) { drive = driveValue; + auto& sh = processorChain.get(); + sh.functionToUse = [this](float x) + { + return std::tanh(drive * x); + }; } /** @@ -60,7 +83,7 @@ void DistortionEffect::setDrive(float driveValue) { * @param mixValue The mix between dry and wet signal (0.0 = dry, 1.0 = wet). */ void DistortionEffect::setMix(float mixValue) { - mix = mixValue; + mix = juce::jlimit(0.0f, 1.0f, mixValue); } /** From 24d07db3b3c43ed893f3b1d7d11054d8909345cb Mon Sep 17 00:00:00 2001 From: MaximeSapountzis Date: Tue, 27 May 2025 15:19:54 +0200 Subject: [PATCH 3/5] Dsp distortion added --- include/DistortionEffect.h | 71 +++++++-------- include/DistortionEffectComponent.h | 6 +- src/components/DistortionEffectComponent.cpp | 53 ++++------- src/effects/DistortionEffect.cpp | 93 +++++++++----------- 4 files changed, 96 insertions(+), 127 deletions(-) diff --git a/include/DistortionEffect.h b/include/DistortionEffect.h index c03764d..7a726ea 100644 --- a/include/DistortionEffect.h +++ b/include/DistortionEffect.h @@ -10,6 +10,7 @@ */ class DistortionEffect : public AbstractEffect { public: + /** * @brief Initializes a new instance of the DistortionEffect class. */ @@ -27,16 +28,16 @@ class DistortionEffect : public AbstractEffect { void apply(const AudioSourceChannelInfo &bufferToFill) override; /** - * @brief Sets the drive amount for the distortion effect. + * @brief Sets the range amount for the distortion effect. * @param driveValue The drive (gain) amount before clipping. */ - void setDrive(float driveValue); + void setRange(float rangeValue); + + static float DistortionEffect::clipWithCurrentRange(float x); - /** - * @brief Sets the mix (dry/wet) of the distortion effect. - * @param mixValue The mix between dry and wet signal (0.0 = dry, 1.0 = wet). - */ - void setMix(float mixValue); + + + void reset() noexcept; /** * @brief Compares the effect with another given effect. @@ -48,6 +49,12 @@ class DistortionEffect : public AbstractEffect { void prepare(const juce::dsp::ProcessSpec& spec); + template + void process(const ProcessContext& context) noexcept; + + float getRange() const; + + /** * @brief Gets the type name of the effect for serialization purposes. * @return A string representing the effect type. @@ -58,44 +65,34 @@ class DistortionEffect : public AbstractEffect { * @brief Serializes the delay effect to a JSON object. * @return JSON object containing serialized effect data. */ - [[nodiscard]] var toJSON() const override { - auto obj = AbstractEffect::toJSON(); - if (auto *dynamicObj = obj.getDynamicObject()) { - dynamicObj->setProperty("drive", drive); - dynamicObj->setProperty("mix", mix); - } - return obj; - } + // [[nodiscard]] var toJSON() const override { + // auto obj = AbstractEffect::toJSON(); + // if (auto *dynamicObj = obj.getDynamicObject()) { + // dynamicObj->setProperty("drive", drive); + // dynamicObj->setProperty("mix", mix); + // } + // return obj; + // } /** * @brief Deserializes the delay effect from a JSON object. * @param json JSON object containing serialized effect data. */ - void fromJSON(const var &json) override { - AbstractEffect::fromJSON(json); - - if (const auto *obj = json.getDynamicObject()) { - drive = static_cast(obj->getProperty("drive")); - mix = static_cast(obj->getProperty("mix")); - } - } + // void fromJSON(const var &json) override { + // AbstractEffect::fromJSON(json); + // + // if (const auto *obj = json.getDynamicObject()) { + // drive = static_cast(obj->getProperty("drive")); + // mix = static_cast(obj->getProperty("mix")); + // } + // } private: - /** - * @brief The drive amount applied before clipping. - */ - float drive; + static float currentRange; - /** - * @brief The dry/wet mix of the effect. - */ - float mix; - enum - { - waveshaperIndex + enum { + waveshaperIndex // [2] }; - - using ShaperType = juce::dsp::WaveShaper>; - juce::dsp::ProcessorChain processorChain; + juce::dsp::ProcessorChain> processorChain; }; diff --git a/include/DistortionEffectComponent.h b/include/DistortionEffectComponent.h index 2811d24..776599c 100644 --- a/include/DistortionEffectComponent.h +++ b/include/DistortionEffectComponent.h @@ -27,10 +27,8 @@ class DistortionEffectComponent : public BasePedalComponent { private: Grid grid; - Slider driveSlider; - Label driveLabel; - Slider mixSlider; - Label mixLabel; + Slider rangeSlider; + Label rangeLabel; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DistortionEffectComponent) }; diff --git a/src/components/DistortionEffectComponent.cpp b/src/components/DistortionEffectComponent.cpp index e93c585..9db12f8 100644 --- a/src/components/DistortionEffectComponent.cpp +++ b/src/components/DistortionEffectComponent.cpp @@ -13,51 +13,32 @@ DistortionEffectComponent::DistortionEffectComponent(AbstractEffect* effect) using Fr = juce::Grid::Fr; grid.templateRows = { Track(Fr(1)), Track(Fr(3)) }; - grid.templateColumns = { Track(Fr(1)), Track(Fr(1)) }; + grid.templateColumns = { Track(Fr(1)) }; grid.items = { - juce::GridItem(driveLabel), - juce::GridItem(mixLabel), - juce::GridItem(driveSlider), - juce::GridItem(mixSlider), + juce::GridItem(rangeLabel), + juce::GridItem(rangeSlider), }; - driveSlider.setSliderStyle(juce::Slider::RotaryVerticalDrag); - driveSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 100, 20); - driveSlider.setColour(juce::Slider::textBoxOutlineColourId, juce::Colours::transparentWhite); - driveSlider.setTextValueSuffix("x"); - driveSlider.setTitle("Drive"); - driveSlider.setRange(0.0, 10.0, 0.1); - driveSlider.setValue(1.0); - driveSlider.onValueChange = [this, distEffect] { - distEffect->setDrive(static_cast(driveSlider.getValue())); + rangeSlider.setSliderStyle(juce::Slider::RotaryVerticalDrag); + rangeSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 100, 20); + rangeSlider.setColour(juce::Slider::textBoxOutlineColourId, juce::Colours::transparentWhite); + rangeSlider.setTextValueSuffix("x"); + rangeSlider.setTitle("Range"); + rangeSlider.setRange(0.0000, 1, 0.0001); + rangeSlider.setValue(0); + rangeSlider.onValueChange = [this, distEffect] { + distEffect->setRange(static_cast(rangeSlider.getValue())); }; - driveLabel.setText("Drive", juce::dontSendNotification); - driveLabel.setJustificationType(juce::Justification::centred); - driveLabel.attachToComponent(&driveSlider, false); + rangeLabel.setText("Range", juce::dontSendNotification); + rangeLabel.setJustificationType(juce::Justification::centred); + rangeLabel.attachToComponent(&rangeSlider, false); - mixSlider.setSliderStyle(juce::Slider::RotaryVerticalDrag); - mixSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 100, 20); - mixSlider.setColour(juce::Slider::textBoxOutlineColourId, juce::Colours::transparentWhite); - mixSlider.setTextValueSuffix("%"); - mixSlider.setTitle("Mix"); - mixSlider.setRange(0.0, 1.0, 0.01); - mixSlider.setValue(0.5); - mixSlider.onValueChange = [this, distEffect] { - distEffect->setMix(static_cast(mixSlider.getValue())); - }; - - mixLabel.setText("Mix", juce::dontSendNotification); - mixLabel.setJustificationType(juce::Justification::centred); - mixLabel.attachToComponent(&mixSlider, false); settingsLayout = new PedalSettingsLayoutComponent(&grid); - addAndMakeVisible(driveSlider); - addAndMakeVisible(driveLabel); - addAndMakeVisible(mixSlider); - addAndMakeVisible(mixLabel); - + addAndMakeVisible(rangeSlider); + addAndMakeVisible(rangeLabel); this->initializePedal(); } } diff --git a/src/effects/DistortionEffect.cpp b/src/effects/DistortionEffect.cpp index dd46110..c1c9ce0 100644 --- a/src/effects/DistortionEffect.cpp +++ b/src/effects/DistortionEffect.cpp @@ -2,18 +2,22 @@ // Lien de la doc : https://juce.com/tutorials/tutorial_dsp_convolution/ +float DistortionEffect::currentRange = 0; + +float DistortionEffect::clipWithCurrentRange(float x) { + if (currentRange >= 1 || currentRange <= 0) { + return juce::jlimit(-1.0f, 1.0f, x); + } + return juce::jlimit(-(1-currentRange), 1-currentRange, x); +} /** * @brief Initializes a new instance of the DistortionEffect class. */ -DistortionEffect::DistortionEffect() - : drive(1.0f), mix(1.0f) -{ +DistortionEffect::DistortionEffect() { + currentRange = 0; effectName = "Distortion"; - auto& sh = processorChain.get(); - sh.functionToUse = [this](float x) - { - return std::tanh(drive * x); - }; + auto &waveshaper = processorChain.template get(); + waveshaper.functionToUse = &clipWithCurrentRange; } /** @@ -23,12 +27,32 @@ DistortionEffect::~DistortionEffect() = default; -void DistortionEffect::prepare(const juce::dsp::ProcessSpec& spec) +void DistortionEffect::prepare (const juce::dsp::ProcessSpec& spec) +{ + processorChain.prepare (spec); +} + + +void DistortionEffect::reset() noexcept { - processorChain.prepare(spec); processorChain.reset(); } +template +void DistortionEffect::process (const ProcessContext& context) noexcept +{ + processorChain.process (context); +} + +void DistortionEffect::setRange(float rangeValue) { + currentRange = rangeValue; + auto& waveshaper = processorChain.template get(); + waveshaper.functionToUse = &clipWithCurrentRange; +} + +float DistortionEffect::getRange() const { + return currentRange; +} /** * @brief Applies the distortion effect to the given audio buffer. @@ -42,49 +66,21 @@ void DistortionEffect::prepare(const juce::dsp::ProcessSpec& spec) * For a more advanced waveshaping approach, see: * https://docs.juce.com/master/structdsp_1_1WaveShaper.html */ -void DistortionEffect::apply (const juce::AudioSourceChannelInfo& bufferToFill) -{ - auto* buffer = bufferToFill.buffer; - const auto start = (size_t) bufferToFill.startSample; - const auto length = (size_t) bufferToFill.numSamples; - - auto fullBlock = juce::dsp::AudioBlock(*buffer, start); - auto subBlock = fullBlock.getSubBlock (0, length); +void DistortionEffect::apply(const juce::AudioSourceChannelInfo& bufferToFill) +{ + if (bufferToFill.buffer == nullptr) return; - dsp::ProcessContextReplacing ctx (subBlock); - processorChain.process (ctx); + juce::dsp::AudioBlock block(*bufferToFill.buffer, + (size_t) bufferToFill.startSample); + auto subBlock = block.getSubBlock(0, (size_t) bufferToFill.numSamples); + juce::dsp::ProcessContextReplacing context(subBlock); - for (int ch = 0; ch < buffer->getNumChannels(); ++ch) - { - auto* write = buffer->getWritePointer (ch, bufferToFill.startSample); - auto* read = buffer->getReadPointer (ch, bufferToFill.startSample); - for (int i = 0; i < bufferToFill.numSamples; ++i) - write[i] = read[i] * (1.0f - mix) + write[i] * mix; - } + process(context); } -/** - * @brief Sets the drive amount for the distortion effect. - * @param driveValue The drive (gain) amount before clipping. - */ -void DistortionEffect::setDrive(float driveValue) { - drive = driveValue; - auto& sh = processorChain.get(); - sh.functionToUse = [this](float x) - { - return std::tanh(drive * x); - }; -} -/** - * @brief Sets the mix (dry/wet) of the distortion effect. - * @param mixValue The mix between dry and wet signal (0.0 = dry, 1.0 = wet). - */ -void DistortionEffect::setMix(float mixValue) { - mix = juce::jlimit(0.0f, 1.0f, mixValue); -} /** * @brief Compares the effect with another given effect. @@ -92,9 +88,6 @@ void DistortionEffect::setMix(float mixValue) { * @return True if the effect is equal to the given effect, false otherwise. */ bool DistortionEffect::operator==(const AbstractEffect *effect) { - if (auto other = dynamic_cast(effect)) { - return drive == other->drive && mix == other->mix; - } - return false; + return dynamic_cast(effect) != nullptr; } From f615edf8df63cac909137998c7045014af55a07e Mon Sep 17 00:00:00 2001 From: MaximeSapountzis Date: Tue, 27 May 2025 15:50:56 +0200 Subject: [PATCH 4/5] Add resize --- src/components/DistortionEffectComponent.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/DistortionEffectComponent.cpp b/src/components/DistortionEffectComponent.cpp index 9db12f8..3519cfe 100644 --- a/src/components/DistortionEffectComponent.cpp +++ b/src/components/DistortionEffectComponent.cpp @@ -39,6 +39,9 @@ DistortionEffectComponent::DistortionEffectComponent(AbstractEffect* effect) addAndMakeVisible(rangeSlider); addAndMakeVisible(rangeLabel); + + + setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT); this->initializePedal(); } } From f7d40ccdb8997087c6540a82a512b37db3ccce25 Mon Sep 17 00:00:00 2001 From: MaximeSapountzis Date: Tue, 27 May 2025 15:58:04 +0200 Subject: [PATCH 5/5] Update JSON serialization for distortion effect to include currentRange --- include/DistortionEffect.h | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/include/DistortionEffect.h b/include/DistortionEffect.h index 7a726ea..26d28fe 100644 --- a/include/DistortionEffect.h +++ b/include/DistortionEffect.h @@ -65,27 +65,25 @@ class DistortionEffect : public AbstractEffect { * @brief Serializes the delay effect to a JSON object. * @return JSON object containing serialized effect data. */ - // [[nodiscard]] var toJSON() const override { - // auto obj = AbstractEffect::toJSON(); - // if (auto *dynamicObj = obj.getDynamicObject()) { - // dynamicObj->setProperty("drive", drive); - // dynamicObj->setProperty("mix", mix); - // } - // return obj; - // } + [[nodiscard]] var toJSON() const override { + auto obj = AbstractEffect::toJSON(); + if (auto *dynamicObj = obj.getDynamicObject()) { + dynamicObj->setProperty("range", currentRange); + } + return obj; + } /** * @brief Deserializes the delay effect from a JSON object. * @param json JSON object containing serialized effect data. */ - // void fromJSON(const var &json) override { - // AbstractEffect::fromJSON(json); - // - // if (const auto *obj = json.getDynamicObject()) { - // drive = static_cast(obj->getProperty("drive")); - // mix = static_cast(obj->getProperty("mix")); - // } - // } + void fromJSON(const var &json) override { + AbstractEffect::fromJSON(json); + + if (const auto *obj = json.getDynamicObject()) { + currentRange = static_cast(obj->getProperty("range")); + } + } private: static float currentRange;