Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 26 additions & 19 deletions include/DistortionEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
#include <JuceHeader.h>
#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.
*/
Expand All @@ -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.
Expand All @@ -44,6 +46,15 @@ class DistortionEffect : public AbstractEffect {
*/
bool operator==(const AbstractEffect *effect) override;


void prepare(const juce::dsp::ProcessSpec& spec);

template <typename ProcessContext>
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.
Expand All @@ -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;
}
Expand All @@ -71,19 +81,16 @@ class DistortionEffect : public AbstractEffect {
AbstractEffect::fromJSON(json);

if (const auto *obj = json.getDynamicObject()) {
drive = static_cast<float>(obj->getProperty("drive"));
mix = static_cast<float>(obj->getProperty("mix"));
currentRange = static_cast<float>(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<juce::dsp::WaveShaper<float>> processorChain;
};
6 changes: 2 additions & 4 deletions include/DistortionEffectComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
};
54 changes: 18 additions & 36 deletions src/components/DistortionEffectComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<float>(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<float>(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<float>(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();
}
}
Expand Down
92 changes: 53 additions & 39 deletions src/effects/DistortionEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,58 @@

// 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<waveshaperIndex>();
waveshaper.functionToUse = &clipWithCurrentRange;
}

/**
* @brief Destroys the instance of the DistortionEffect class.
*/
DistortionEffect::~DistortionEffect() = default;



void DistortionEffect::prepare (const juce::dsp::ProcessSpec& spec)
{
processorChain.prepare (spec);
}


void DistortionEffect::reset() noexcept
{
processorChain.reset();
}

template <typename ProcessContext>
void DistortionEffect::process (const ProcessContext& context) noexcept
{
processorChain.process (context);
}

void DistortionEffect::setRange(float rangeValue) {
currentRange = rangeValue;
auto& waveshaper = processorChain.template get<waveshaperIndex>();
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.
Expand All @@ -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<float> block(*bufferToFill.buffer,
(size_t) bufferToFill.startSample);
auto subBlock = block.getSubBlock(0, (size_t) bufferToFill.numSamples);
juce::dsp::ProcessContextReplacing<float> 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<const DistortionEffect *>(effect)) {
return drive == other->drive && mix == other->mix;
}
return false;
return dynamic_cast<const DistortionEffect *>(effect) != nullptr;
}

Loading