diff --git a/include/DistortionEffect.h b/include/DistortionEffect.h index 5273391..26d28fe 100644 --- a/include/DistortionEffect.h +++ b/include/DistortionEffect.h @@ -3,12 +3,14 @@ #include #include "AbstractEffect.h" + /** * @brief Represents a distortion effect that applies non-linear clipping to the audio stream. * Inherits from the AbstractEffect class. */ class DistortionEffect : public AbstractEffect { public: + /** * @brief Initializes a new instance of the DistortionEffect class. */ @@ -26,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. @@ -44,6 +46,15 @@ class DistortionEffect : public AbstractEffect { */ bool operator==(const AbstractEffect *effect) override; + + 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. @@ -57,8 +68,7 @@ class DistortionEffect : public AbstractEffect { [[nodiscard]] var toJSON() const override { auto obj = AbstractEffect::toJSON(); if (auto *dynamicObj = obj.getDynamicObject()) { - dynamicObj->setProperty("drive", drive); - dynamicObj->setProperty("mix", mix); + dynamicObj->setProperty("range", currentRange); } return obj; } @@ -71,19 +81,16 @@ class DistortionEffect : public AbstractEffect { AbstractEffect::fromJSON(json); if (const auto *obj = json.getDynamicObject()) { - drive = static_cast(obj->getProperty("drive")); - mix = static_cast(obj->getProperty("mix")); + currentRange = static_cast(obj->getProperty("range")); } } 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 // [2] + }; + 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 ef1c4bd..3519cfe 100644 --- a/src/components/DistortionEffectComponent.cpp +++ b/src/components/DistortionEffectComponent.cpp @@ -13,53 +13,35 @@ 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); - setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); + setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT); this->initializePedal(); } } diff --git a/src/effects/DistortionEffect.cpp b/src/effects/DistortionEffect.cpp index 537701e..c1c9ce0 100644 --- a/src/effects/DistortionEffect.cpp +++ b/src/effects/DistortionEffect.cpp @@ -2,14 +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() { - // Default drive amount and mix + currentRange = 0; effectName = "Distortion"; - drive = 1.0f; - mix = 0.5f; + auto &waveshaper = processorChain.template get(); + waveshaper.functionToUse = &clipWithCurrentRange; } /** @@ -17,6 +25,35 @@ DistortionEffect::DistortionEffect() { */ DistortionEffect::~DistortionEffect() = default; + + +void DistortionEffect::prepare (const juce::dsp::ProcessSpec& spec) +{ + processorChain.prepare (spec); +} + + +void DistortionEffect::reset() noexcept +{ + 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. * @param bufferToFill The audio buffer to apply the effect to. @@ -29,51 +66,28 @@ 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]; - - // Appliquer un gain d'entrée pour amplifier le signal - const float preGain = 50.0f; // Ajustez ce gain pour contrôler l'intensité - const float shapedL = std::tanh(inL * drive * preGain); - const float shapedR = std::tanh(inR * drive * preGain); - - // Mélanger le signal dry et wet - leftBuffer[i] = inL * (1.0f - mix) + shapedL * mix; - rightBuffer[i] = inR * (1.0f - mix) + shapedR * 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; -} +void DistortionEffect::apply(const juce::AudioSourceChannelInfo& bufferToFill) +{ + if (bufferToFill.buffer == nullptr) return; -/** - * @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 = mixValue; + juce::dsp::AudioBlock block(*bufferToFill.buffer, + (size_t) bufferToFill.startSample); + auto subBlock = block.getSubBlock(0, (size_t) bufferToFill.numSamples); + juce::dsp::ProcessContextReplacing context(subBlock); + + process(context); } + + + /** * @brief Compares the effect with another given effect. * @param effect The effect to compare with. * @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; }