From 9e27eca6dc1bb2c4e8b6c86a2e31b926d321d667 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 27 May 2025 14:01:31 +0200 Subject: [PATCH 1/4] up --- src/utils/ResourceManager.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/utils/ResourceManager.cpp b/src/utils/ResourceManager.cpp index 42cc164..e6ef989 100644 --- a/src/utils/ResourceManager.cpp +++ b/src/utils/ResourceManager.cpp @@ -1,23 +1,23 @@ #include "ResourceManager.h" -ResourceManager::ResourceManager() {} -ResourceManager::~ResourceManager() {} +ResourceManager::ResourceManager() = default; +ResourceManager::~ResourceManager() = default; -juce::Image ResourceManager::loadImage(const juce::String& relativePath) +Image ResourceManager::loadImage(const String& relativePath) { - juce::File imageFile; + File imageFile; #if JUCE_MAC - imageFile = juce::File::getSpecialLocation(juce::File::currentApplicationFile) - .getParentDirectory() - .getParentDirectory() - .getParentDirectory() - .getChildFile(relativePath); + imageFile = File::getSpecialLocation(File::currentApplicationFile) + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile(relativePath); #else imageFile = juce::File::getCurrentWorkingDirectory().getChildFile(relativePath); #endif - juce::Image image = juce::ImageFileFormat::loadFrom(imageFile); + Image image = ImageFileFormat::loadFrom(imageFile); if (image.isNull()) { From cfcef326932bed182166367a1b3a9cca814614f7 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 27 May 2025 16:34:22 +0200 Subject: [PATCH 2/4] Add effect --- CMakeLists.txt | 3 + include/DistortionEffect.h | 2 +- include/EqualizerEffect.h | 29 ++++++++ include/EqualizerEffectComponent.h | 24 +++++++ src/EffectComponentFactory.cpp | 6 ++ src/Pedalboard.cpp | 2 + src/components/EqualizerEffectComponent.cpp | 75 +++++++++++++++++++++ src/effects/EqualizerEffect.cpp | 69 +++++++++++++++++++ 8 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 include/EqualizerEffect.h create mode 100644 include/EqualizerEffectComponent.h create mode 100644 src/components/EqualizerEffectComponent.cpp create mode 100644 src/effects/EqualizerEffect.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 113d361..6160d8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,8 @@ else() endif() + + target_sources(kAmp PRIVATE ${SOURCES}) @@ -111,6 +113,7 @@ target_link_libraries(kAmp juce::juce_audio_basics juce::juce_audio_utils juce::juce_gui_extra + juce::juce_dsp PUBLIC juce::juce_recommended_config_flags juce::juce_recommended_lto_flags diff --git a/include/DistortionEffect.h b/include/DistortionEffect.h index 26d28fe..2d47b54 100644 --- a/include/DistortionEffect.h +++ b/include/DistortionEffect.h @@ -33,7 +33,7 @@ class DistortionEffect : public AbstractEffect { */ void setRange(float rangeValue); - static float DistortionEffect::clipWithCurrentRange(float x); + static float clipWithCurrentRange(float x); diff --git a/include/EqualizerEffect.h b/include/EqualizerEffect.h new file mode 100644 index 0000000..37334aa --- /dev/null +++ b/include/EqualizerEffect.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include "AbstractEffect.h" +#include + + +class EqualizerEffect : public AbstractEffect { +public: + EqualizerEffect(); + ~EqualizerEffect() override = default; + + void apply(const AudioSourceChannelInfo &bufferToFill) override; + void setGain(int bandIndex, float gain); + float getGain(int bandIndex) const; + bool operator==(const AbstractEffect* effect) override; + + [[nodiscard]] String getEffectType() const override { return "EqualizerEffect"; } + + [[nodiscard]] var toJSON() const override; + void fromJSON(const var &json) override; + +private: + static constexpr int numBands = 10; + float bandGains[numBands]; // Gain en dB pour chaque bande + std::vector> filters; + + void updateFilters(); +}; diff --git a/include/EqualizerEffectComponent.h b/include/EqualizerEffectComponent.h new file mode 100644 index 0000000..c582fba --- /dev/null +++ b/include/EqualizerEffectComponent.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "BasePedalComponent.h" +#include "EqualizerEffect.h" + +class EqualizerEffectComponent : public BasePedalComponent { +public: + explicit EqualizerEffectComponent(AbstractEffect* effect); + ~EqualizerEffectComponent() override = default; + +private: + EqualizerEffect* eqEffect; + Slider sliders[10]; + Label labels[10]; + + Grid grid; + + void sliderValueChanged(Slider* slider); + + void resized() override; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EqualizerEffectComponent) +}; diff --git a/src/EffectComponentFactory.cpp b/src/EffectComponentFactory.cpp index 2dec27e..ffaf897 100644 --- a/src/EffectComponentFactory.cpp +++ b/src/EffectComponentFactory.cpp @@ -3,6 +3,8 @@ #include "DistortionEffect.h" #include "DistortionEffectComponent.h" #include "EffectComponentFactory.h" +#include "EqualizerEffect.h" +#include "EqualizerEffectComponent.h" #include "NoiseGateEffect.h" #include "NoiseGateEffectComponent.h" @@ -13,6 +15,10 @@ EffectComponent* EffectComponentFactory::CreateEffectComponent(AbstractEffect* e if (dynamic_cast(effect) != nullptr) { return new DistortionEffectComponent(effect); } + if (dynamic_cast(effect) != nullptr) + { + return new EqualizerEffectComponent(effect); + } if (dynamic_cast(effect) != nullptr) { return new NoiseGateEffectComponent(effect); } diff --git a/src/Pedalboard.cpp b/src/Pedalboard.cpp index cebec8d..22203d9 100644 --- a/src/Pedalboard.cpp +++ b/src/Pedalboard.cpp @@ -1,11 +1,13 @@ #include "DelayEffect.h" #include "DistortionEffect.h" #include "Pedalboard.h" +#include "EqualizerEffect.h" #include "NoiseGateEffect.h" Pedalboard::Pedalboard() { this->append(new DelayEffect()); this->append(new DistortionEffect()); + this->append(new EqualizerEffect()); this->append(new NoiseGateEffect()); } diff --git a/src/components/EqualizerEffectComponent.cpp b/src/components/EqualizerEffectComponent.cpp new file mode 100644 index 0000000..fea72f0 --- /dev/null +++ b/src/components/EqualizerEffectComponent.cpp @@ -0,0 +1,75 @@ +#include "EqualizerEffectComponent.h" +#include + +#include "EqualizerEffectComponent.h" + + +EqualizerEffectComponent::EqualizerEffectComponent(AbstractEffect* effect) + : BasePedalComponent(effect), eqEffect(dynamic_cast(effect)) { + + jassert(eqEffect != nullptr); + + primaryColor = juce::Colours::orange; + + const char* freqLabels[10] = { "31Hz", "62Hz", "125Hz", "250Hz", "500Hz", "1k", "2k", "4k", "8k", "16k" }; + + using Track = juce::Grid::TrackInfo; + using Fr = juce::Grid::Fr; + + // 2 lignes : sliders + labels + grid.templateRows = { Track(Fr(4)), Track(Fr(1)) }; + grid.templateColumns = { Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), + Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)) }; + + for (int i = 0; i < 10; ++i) { + sliders[i].setSliderStyle(juce::Slider::LinearVertical); + sliders[i].setTextBoxStyle(juce::Slider::TextBoxBelow, false, 50, 20); + sliders[i].setRange(-24.0, 24.0, 0.1); + sliders[i].setValue(0.0); + sliders[i].setColour(juce::Slider::textBoxOutlineColourId, juce::Colours::transparentWhite); + sliders[i].onValueChange = [this, i] { + eqEffect->setGain(i, sliders[i].getValue()); + }; + sliders[i].setTitle(freqLabels[i]); + + labels[i].setText(freqLabels[i], juce::dontSendNotification); + labels[i].setJustificationType(juce::Justification::centred); + labels[i].attachToComponent(&sliders[i], false); + + grid.items.add(juce::GridItem(sliders[i])); + grid.items.add(juce::GridItem(labels[i])); + + addAndMakeVisible(sliders[i]); + addAndMakeVisible(labels[i]); + } + + settingsLayout = new PedalSettingsLayoutComponent(&grid); + + setSize(400, 300); + this->initializePedal(); +} + + + +void EqualizerEffectComponent::sliderValueChanged(juce::Slider* slider) { + for (int i = 0; i < 10; ++i) { + if (slider == &sliders[i]) { + eqEffect->setGain(i, slider->getValue()); + break; + } + } +} + +void EqualizerEffectComponent::resized() { + auto bounds = getLocalBounds().reduced(10); + + int sliderWidth = bounds.getWidth() / 10; + int sliderHeight = bounds.getHeight() - 70; // laisser place pour labels + bouton + + // Position sliders et labels + for (int i = 0; i < 10; ++i) { + auto sliderArea = bounds.removeFromLeft(sliderWidth); + sliders[i].setBounds(sliderArea.removeFromTop(sliderHeight)); + labels[i].setBounds(sliderArea); + } +} diff --git a/src/effects/EqualizerEffect.cpp b/src/effects/EqualizerEffect.cpp new file mode 100644 index 0000000..0343ee0 --- /dev/null +++ b/src/effects/EqualizerEffect.cpp @@ -0,0 +1,69 @@ +#include "EqualizerEffect.h" + +EqualizerEffect::EqualizerEffect() { + effectName = "Equalizer"; + filters.resize(numBands); + for (int i = 0; i < numBands; ++i) bandGains[i] = 0.0f; + updateFilters(); +} + +void EqualizerEffect::apply(const juce::AudioSourceChannelInfo &bufferToFill) { + juce::dsp::AudioBlock block(*bufferToFill.buffer); + juce::dsp::ProcessContextReplacing context(block); + + for (int i = 0; i < numBands; ++i) { + filters[i].process(context); + } +} + +void EqualizerEffect::setGain(int bandIndex, float gain) { + if (bandIndex >= 0 && bandIndex < numBands) { + bandGains[bandIndex] = gain; + updateFilters(); + } +} + +float EqualizerEffect::getGain(int bandIndex) const { + return (bandIndex >= 0 && bandIndex < numBands) ? bandGains[bandIndex] : 0.0f; +} + +void EqualizerEffect::updateFilters() { + // Fréquences fixes typiques d'un égaliseur 10 bandes + const float freqs[10] = {31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000}; + constexpr float sampleRate = 44100.0f; + + for (int i = 0; i < numBands; ++i) { + filters[i].reset(); + auto coeffs = juce::dsp::IIR::Coefficients::makePeakFilter( + sampleRate, freqs[i], 1.0f, juce::Decibels::decibelsToGain(bandGains[i]) + ); + filters[i].coefficients = coeffs; + } +} + +bool EqualizerEffect::operator==(const AbstractEffect* effect) { + return this == effect; +} + +juce::var EqualizerEffect::toJSON() const { + auto obj = AbstractEffect::toJSON(); + if (auto *dynamicObj = obj.getDynamicObject()) { + juce::Array gains; + for (float g : bandGains) gains.add(g); + dynamicObj->setProperty("bandGains", gains); + } + return obj; +} + +void EqualizerEffect::fromJSON(const juce::var &json) { + AbstractEffect::fromJSON(json); + if (const auto* obj = json.getDynamicObject()) { + const auto& gains = obj->getProperty("bandGains"); + if (auto* arr = gains.getArray()) { + for (int i = 0; i < juce::jmin((int)arr->size(), numBands); ++i) { + bandGains[i] = static_cast((*arr)[i]); + } + updateFilters(); + } + } +} From 653cb2b5cc1b08cb18eaa5031bda58806a257b28 Mon Sep 17 00:00:00 2001 From: NottarJohnny Date: Tue, 27 May 2025 17:10:29 +0200 Subject: [PATCH 3/4] Fixed displaying of EqualizerEffectComponent. --- include/EqualizerEffectComponent.h | 2 -- src/components/EqualizerEffectComponent.cpp | 39 ++++++++++----------- src/effects/EqualizerEffect.cpp | 1 + 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/include/EqualizerEffectComponent.h b/include/EqualizerEffectComponent.h index c582fba..32ecbe1 100644 --- a/include/EqualizerEffectComponent.h +++ b/include/EqualizerEffectComponent.h @@ -18,7 +18,5 @@ class EqualizerEffectComponent : public BasePedalComponent { void sliderValueChanged(Slider* slider); - void resized() override; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EqualizerEffectComponent) }; diff --git a/src/components/EqualizerEffectComponent.cpp b/src/components/EqualizerEffectComponent.cpp index fea72f0..bcd0b95 100644 --- a/src/components/EqualizerEffectComponent.cpp +++ b/src/components/EqualizerEffectComponent.cpp @@ -17,7 +17,7 @@ EqualizerEffectComponent::EqualizerEffectComponent(AbstractEffect* effect) using Fr = juce::Grid::Fr; // 2 lignes : sliders + labels - grid.templateRows = { Track(Fr(4)), Track(Fr(1)) }; + grid.templateRows = { Track(Fr(1)), Track(Fr(3)) }; grid.templateColumns = { Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)), Track(Fr(1)) }; @@ -27,22 +27,33 @@ EqualizerEffectComponent::EqualizerEffectComponent(AbstractEffect* effect) sliders[i].setRange(-24.0, 24.0, 0.1); sliders[i].setValue(0.0); sliders[i].setColour(juce::Slider::textBoxOutlineColourId, juce::Colours::transparentWhite); - sliders[i].onValueChange = [this, i] { - eqEffect->setGain(i, sliders[i].getValue()); + sliders[i].onValueChange = [this, i] + { + if (static_cast(sliders[i].getValue())) { + eqEffect->setGain(i, static_cast(sliders[i].getValue())); + } }; sliders[i].setTitle(freqLabels[i]); + sliders[i].setBounds(i * (getWidth() / 10), 0, getWidth() / 10, (getHeight() / 5) * 4); labels[i].setText(freqLabels[i], juce::dontSendNotification); labels[i].setJustificationType(juce::Justification::centred); labels[i].attachToComponent(&sliders[i], false); - - grid.items.add(juce::GridItem(sliders[i])); - grid.items.add(juce::GridItem(labels[i])); + labels[i].setBounds(i * (getWidth() / 10), 0, getWidth() / 10, getHeight() / 5); addAndMakeVisible(sliders[i]); addAndMakeVisible(labels[i]); } + for (int i = 0; i < 10; ++i) + { + grid.items.add(GridItem(labels[i])); + } + for (int i = 0; i < 10; ++i) + { + grid.items.add(juce::GridItem(sliders[i])); + } + settingsLayout = new PedalSettingsLayoutComponent(&grid); setSize(400, 300); @@ -58,18 +69,4 @@ void EqualizerEffectComponent::sliderValueChanged(juce::Slider* slider) { break; } } -} - -void EqualizerEffectComponent::resized() { - auto bounds = getLocalBounds().reduced(10); - - int sliderWidth = bounds.getWidth() / 10; - int sliderHeight = bounds.getHeight() - 70; // laisser place pour labels + bouton - - // Position sliders et labels - for (int i = 0; i < 10; ++i) { - auto sliderArea = bounds.removeFromLeft(sliderWidth); - sliders[i].setBounds(sliderArea.removeFromTop(sliderHeight)); - labels[i].setBounds(sliderArea); - } -} +} \ No newline at end of file diff --git a/src/effects/EqualizerEffect.cpp b/src/effects/EqualizerEffect.cpp index 0343ee0..36595c0 100644 --- a/src/effects/EqualizerEffect.cpp +++ b/src/effects/EqualizerEffect.cpp @@ -1,4 +1,5 @@ #include "EqualizerEffect.h" +#include EqualizerEffect::EqualizerEffect() { effectName = "Equalizer"; From 39918dafe450af116efd532545912f445d669246 Mon Sep 17 00:00:00 2001 From: NottarJohnny Date: Fri, 30 May 2025 20:37:41 +0200 Subject: [PATCH 4/4] Fixed EqualizerEffect assertion error, fixed pedals components rendering on mac and changed channel management on effects applying. --- .idea/vcs.xml | 2 ++ src/components/DelayEffectComponent.cpp | 3 +- src/components/DistortionEffectComponent.cpp | 3 +- src/components/EqualizerEffectComponent.cpp | 2 +- src/components/NoiseGateEffectComponent.cpp | 3 +- src/effects/DelayEffect.cpp | 29 +++++++------------- src/effects/DistortionEffect.cpp | 1 + src/effects/EqualizerEffect.cpp | 16 ++++++++--- 8 files changed, 29 insertions(+), 30 deletions(-) diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1dd..480f52b 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,7 @@ + + \ No newline at end of file diff --git a/src/components/DelayEffectComponent.cpp b/src/components/DelayEffectComponent.cpp index 9090256..a38ade3 100644 --- a/src/components/DelayEffectComponent.cpp +++ b/src/components/DelayEffectComponent.cpp @@ -53,9 +53,8 @@ DelayEffectComponent::DelayEffectComponent(AbstractEffect* effect) : BasePedalCo addAndMakeVisible(delaySlider); addAndMakeVisible(delayLabel); - setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); - this->initializePedal(); + setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); } } diff --git a/src/components/DistortionEffectComponent.cpp b/src/components/DistortionEffectComponent.cpp index 3519cfe..0154fb3 100644 --- a/src/components/DistortionEffectComponent.cpp +++ b/src/components/DistortionEffectComponent.cpp @@ -40,9 +40,8 @@ DistortionEffectComponent::DistortionEffectComponent(AbstractEffect* effect) addAndMakeVisible(rangeSlider); addAndMakeVisible(rangeLabel); - - setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT); this->initializePedal(); + setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT); } } diff --git a/src/components/EqualizerEffectComponent.cpp b/src/components/EqualizerEffectComponent.cpp index bcd0b95..ab116e2 100644 --- a/src/components/EqualizerEffectComponent.cpp +++ b/src/components/EqualizerEffectComponent.cpp @@ -56,8 +56,8 @@ EqualizerEffectComponent::EqualizerEffectComponent(AbstractEffect* effect) settingsLayout = new PedalSettingsLayoutComponent(&grid); - setSize(400, 300); this->initializePedal(); + setSize(400, 300); } diff --git a/src/components/NoiseGateEffectComponent.cpp b/src/components/NoiseGateEffectComponent.cpp index 3c748d1..0389c56 100644 --- a/src/components/NoiseGateEffectComponent.cpp +++ b/src/components/NoiseGateEffectComponent.cpp @@ -75,9 +75,8 @@ NoiseGateEffectComponent::NoiseGateEffectComponent(AbstractEffect* effect): Base addAndMakeVisible(releaseSlider); addAndMakeVisible(releaseLabel); - setSize(300, 300); - this->initializePedal(); + setSize(300, 300); } } diff --git a/src/effects/DelayEffect.cpp b/src/effects/DelayEffect.cpp index dbf68a2..fd19713 100644 --- a/src/effects/DelayEffect.cpp +++ b/src/effects/DelayEffect.cpp @@ -9,15 +9,9 @@ DelayEffect::DelayEffect() { DelayEffect::~DelayEffect() = default; void DelayEffect::apply(const AudioSourceChannelInfo &bufferToFill) { - if (delay > 0 && rate > 0) - { - auto* leftBuffer = bufferToFill.buffer->getWritePointer(0); - auto* rightBuffer = bufferToFill.buffer->getNumChannels() > 1 - ? bufferToFill.buffer->getWritePointer(1) - : nullptr; - const auto numSamples = bufferToFill.numSamples; - - // TODO : Remove constants and check if they can be recovered with JUCE. + if (delay > 0 && rate > 0) { + const int numChannels = bufferToFill.buffer->getNumChannels(); + const int numSamples = bufferToFill.numSamples; constexpr auto sampleRate = 44100.0; const auto delaySamples = static_cast(delay * sampleRate / 1000.0f); @@ -26,19 +20,16 @@ void DelayEffect::apply(const AudioSourceChannelInfo &bufferToFill) { writePosition = 0; } + for (int i = 0; i < numSamples; ++i) { auto delayedSample = circularBuffer[writePosition]; - - auto inputSampleLeft = leftBuffer[i]; - leftBuffer[i] = inputSampleLeft + delayedSample * (rate / 100.0f); - - if (rightBuffer) { - auto inputSampleRight = rightBuffer[i]; - rightBuffer[i] = inputSampleRight + delayedSample * (rate / 100.0f); + for (int channel = 0; channel < numChannels; ++channel) + { + auto* channelBuffer = bufferToFill.buffer->getWritePointer(channel); + auto inputSample = channelBuffer[i]; + channelBuffer[i] = inputSample + delayedSample * (rate / 100.0f); + circularBuffer[writePosition] = inputSample; } - - circularBuffer[writePosition] = inputSampleLeft; - writePosition = (writePosition + 1) % delaySamples; } } diff --git a/src/effects/DistortionEffect.cpp b/src/effects/DistortionEffect.cpp index c1c9ce0..14e7b50 100644 --- a/src/effects/DistortionEffect.cpp +++ b/src/effects/DistortionEffect.cpp @@ -75,6 +75,7 @@ void DistortionEffect::apply(const juce::AudioSourceChannelInfo& bufferToFill) (size_t) bufferToFill.startSample); auto subBlock = block.getSubBlock(0, (size_t) bufferToFill.numSamples); juce::dsp::ProcessContextReplacing context(subBlock); + subBlock.multiplyBy(1.0f / std::max(1.0f - currentRange, 0.01f)); // Normalisation du volume process(context); } diff --git a/src/effects/EqualizerEffect.cpp b/src/effects/EqualizerEffect.cpp index 36595c0..db7d960 100644 --- a/src/effects/EqualizerEffect.cpp +++ b/src/effects/EqualizerEffect.cpp @@ -9,11 +9,19 @@ EqualizerEffect::EqualizerEffect() { } void EqualizerEffect::apply(const juce::AudioSourceChannelInfo &bufferToFill) { - juce::dsp::AudioBlock block(*bufferToFill.buffer); - juce::dsp::ProcessContextReplacing context(block); + auto* buffer = bufferToFill.buffer; + const int numChannels = buffer->getNumChannels(); + const int numSamples = bufferToFill.numSamples; - for (int i = 0; i < numBands; ++i) { - filters[i].process(context); + juce::dsp::AudioBlock block(*buffer); + + for (int channel = 0; channel < numChannels; ++channel) { + auto channelBlock = block.getSingleChannelBlock((size_t)channel); + juce::dsp::ProcessContextReplacing context(channelBlock); + + for (int i = 0; i < numBands; ++i) { + filters[i].process(context); + } } }