/* ============================================================================== This file contains the basic framework code for a JUCE plugin editor. ============================================================================== */ #pragma once #include #include #include "PluginProcessor.h" #include "AXIOMDesignSystem.h" //============================================================================== /** */ using AXIOM::DesignSystem; using Components = DesignSystem::Components; using SliderStyles = Components::SliderStyles; class SpectrumAnalyzer { public: SpectrumAnalyzer(AudioFIFO &fifoRef, CrystalizerEQAudioProcessor &processor) : audioFIFO(fifoRef), audioProcessor(processor) { fftData.resize(2 * FFTSIZE); magnitudes.resize(BINS); magnitudesDb.resize(BINS); emaSmoothedMagnitudesDb.resize(BINS); freqSmoothedMagnitudesDb.resize(BINS); sampleRate = audioProcessor.getSampleRate(); deltaF = sampleRate / static_cast(FFTSIZE); freqPerBin.resize(BINS); peakHoldMagnitudesDb.resize(BINS); DELTAT = static_cast(HOPSIZE) / static_cast(sampleRate); renderValuesDb.resize(BINS); } ~SpectrumAnalyzer() = default; AudioFIFO &audioFIFO; CrystalizerEQAudioProcessor &audioProcessor; static constexpr int FFTSIZE = 4096; static constexpr int BINS = FFTSIZE / 2 + 1; static constexpr int HOPSIZE = FFTSIZE / 2; juce::Array fftFrame; //CHANGE FOR LOWER/HIGHER EMA-SMOOTHING //LOWER VALUE -> MORE SMOOTHING/SLOW RESPONSE -- HIGHER VALUE -> LESS SMOOTHING/FAST RESPONSE //MAX VALUE = 1.f float smoothingFactor = 0.5f; void processSamples(); void getFftFrame(juce::Array &samples); void applyWindowOnFftFrame(std::vector &fullFrame); juce::dsp::WindowingFunction window { FFTSIZE, juce::dsp::WindowingFunction::hann }; void processWindowedFrame(std::vector &windowedFrame); static constexpr int FFTORDER = 12; juce::dsp::FFT fft{FFTORDER}; std::vector fftData; const float MINDB = -90.f; const float MAXDB = 0.f; std::vector magnitudes; std::vector magnitudesDb; std::vector emaSmoothedMagnitudesDb; std::vector freqSmoothedMagnitudesDb; std::vector peakHoldMagnitudesDb; std::vector renderValuesDb; double deltaF = 0; double sampleRate = 44100; //CHANGE DENOMINATOR FOR LOWER/HIGHER FREQ-SMOOTHING //HIGHER VALUE -> HIGHER SMOOTHING -- LESS VALUE -> LESS SMOOTHING const float BANDWIDTHOCT = 1.f / 6.f; const float OCTAVERADIUS = BANDWIDTHOCT / 2.f; std::vector freqPerBin; //CHANGE FOR FASTER FALLOFF -- VALUE IS IN DB/S const float FALLOFFRATE = 240.f; float DELTAT = 0.f; float tempPeakHoldDB = 0.f; const float MINFREQ = 20.0f; const float MAXFREQ = static_cast(sampleRate) / 2; void fillFftDataFromFrame(std::vector &windowedFrame); void buildMagnitudeSpectrum(); void convertToDb(); void applySmoothing(); void applyEMA(); void applyFreqSmoothing(); void applyPeakHoldAndFalloff(); std::vector getRenderValues(); }; class CrystalizerEQAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::Timer { public: CrystalizerEQAudioProcessorEditor(CrystalizerEQAudioProcessor &); ~CrystalizerEQAudioProcessorEditor() override; void paintAnalyzer(juce::Graphics &g); void paintModeBoxBorders(juce::Graphics &g); void paintBorderLines(juce::Graphics &g); //=================================================================== void timerCallback(); void paint(juce::Graphics &) override; void resized() override; void resetAllCheckboxes(); using SliderAttach = juce::AudioProcessorValueTreeState::SliderAttachment; using ButtonAttach = juce::AudioProcessorValueTreeState::ButtonAttachment; using ComboBoxAttach = juce::AudioProcessorValueTreeState::ComboBoxAttachment; juce::Slider lowBandFreqSlider, lowBandSlopeSlider, lowBandGainSlider, lowBandQSlider, peak1FreqSlider, peak1GainSlider, peak1QSlider, peak2FreqSlider, peak2GainSlider, peak2QSlider, peak3FreqSlider, peak3GainSlider, peak3QSlider, highBandFreqSlider, highBandSlopeSlider, highBandGainSlider, highBandQSlider, inputSlider, outputSlider; const juce::Array sliders = { &lowBandGainSlider, &lowBandFreqSlider, &lowBandQSlider, &lowBandSlopeSlider, &peak1GainSlider, &peak1FreqSlider, &peak1QSlider, &peak2GainSlider, &peak2FreqSlider, &peak2QSlider, &peak3GainSlider, &peak3FreqSlider, &peak3QSlider, &highBandGainSlider, &highBandFreqSlider, &highBandQSlider, &highBandSlopeSlider, &inputSlider, &outputSlider }; std::unique_ptr lowBandFreqAttach, lowBandSlopeAttach, lowBandGainAttach, lowBandQAttach, peak1FreqAttach, peak1GainAttach, peak1QAttach, peak2FreqAttach, peak2GainAttach, peak2QAttach, peak3FreqAttach, peak3GainAttach, peak3QAttach, highBandFreqAttach, highBandSlopeAttach, highBandGainAttach, highBandQAttach, inputAttach, outputAttach; juce::Label titleLabel, lowBandFreqLabel, lowBandSlopeLabel, lowBandGainLabel, lowBandQLabel, lowBandModeLabel, low12, low24, low36, low48, lowdBOctLabel, peak1FreqLabel, peak1GainLabel, peak1QLabel, peak2FreqLabel, peak2GainLabel, peak2QLabel, peak3FreqLabel, peak3GainLabel, peak3QLabel, highBandFreqLabel, highBandSlopeLabel, highBandGainLabel, highBandQLabel, highBandModeLabel, high12, high24, high36, high48, highdBOctLabel, inputLabel, outputLabel, presetBoxLabel; const juce::Array sliderLabels = { &lowBandFreqLabel, &lowBandSlopeLabel, &lowBandGainLabel, &lowBandQLabel, &lowBandModeLabel, &lowdBOctLabel, &peak1FreqLabel, &peak1GainLabel, &peak1QLabel, &peak2FreqLabel, &peak2GainLabel, &peak2QLabel, &peak3FreqLabel, &peak3GainLabel, &peak3QLabel, &highBandFreqLabel, &highBandSlopeLabel, &highBandGainLabel, &highBandQLabel, &highBandModeLabel, &highdBOctLabel, &inputLabel, &outputLabel }; const juce::Array slopeLabels = { &low12, &low24, &low36, &low48, &high12, &high24, &high36, &high48 }; juce::TextButton resetButton, savePresetButton, deletePresetButton; juce::ToggleButton masterBypassButton, crystalizeButton, peak1BypassButton, peak2BypassButton, peak3BypassButton, presetMenuButton; const juce::Array peakBypassButtons = { &peak1BypassButton, &peak2BypassButton, &peak3BypassButton }; juce::ComboBox presetBox; juce::Component lowBandModeBox, highBandModeBox; juce::Array lowBandModeButtons, highBandModeButtons; std::array lowBandBools, highBandBools; juce::ToggleButton lowBypass, lowCut, lowBell, lowShelf, highBypass, highCut, highBell, highShelf; DesignSystem::ClickableTextEditor presetNameInput; private: // This reference is provided as a quick way for your editor to // access the processor object that created it. CrystalizerEQAudioProcessor &audioProcessor; void setKnobVisibility(); void animateCrystalizeButton(); void setupSliders(); void setupSliderTextBoxes(); void handleLowBandModes(); void handleHighBandModes(); void setupAttachments(); void setupDisplayNames(); void setupToggleButtons(); void disableLowBand(float target); void disableLowMidBand(float target); void disableMidBand(float target); void disableHighMidBand(float target); void disableHighBand(float target); void disableEverything(float target); void addComponentsToLayout(); void setupLabels(); void setupFontsWithColours(); void setupEventListeners(); void initPresetSystem(); void updateFrequencyRanges(); void updateModeButtons(); std::unique_ptr baseLookAndFeel; std::unique_ptr gainLookAndFeel; std::unique_ptr freqQLookAndFeel; std::unique_ptr slopeLookAndFeel; std::unique_ptr globalLookAndFeel; std::unique_ptr bypassButtonLookAndFeel; std::unique_ptr presetMenuButtonLookAndFeel; std::unique_ptr lowBandButtonLookAndFeel; std::unique_ptr highBandButtonLookAndFeel; std::unique_ptr svgToggleButtonLookAndFeel; std::unique_ptr presetMenuLookAndFeel; std::unique_ptr presetButtonLookAndFeel; std::unique_ptr presetComboBoxLookAndFeel; std::unique_ptr presetInputLookAndFeel; //SPECRTRUM ANALYZER SpectrumAnalyzer spectrumAnalyzer; juce::Rectangle analyzerRect; Component headerBar; Component mainPanel; Component footerBar; Component presetArea; Component presetMenu; Component analyzerArea; Component filterArea; Component lowFilterArea; Component lowMidFilterArea; Component midFilterArea; Component highMidFilterArea; Component highFilterArea; const juce::Array filterAreas{ &lowFilterArea, &lowMidFilterArea, &midFilterArea, &highMidFilterArea, &highFilterArea }; Component globalControlArea; void scalePluginWindow(juce::Rectangle area); void setupMainGrid(juce::Rectangle area); void setupLowBandLayout(); void setupLowMidBandLayout(); void setupMidBandLayout(); void setupHighMidBandLayout(); void setupHighBandLayout(); void setupHeader(); void setupBody(); void setupFooter(); juce::Array getReferenceCell(); const float gainMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Gain); const float freqMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Freq); const float qMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Q); const float slopeMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Slope); const float globalMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Global); const float filterAreaMargin = 15.0f; bool isAnimatingCrystalize = false; bool isFadingToActive = false; std::unique_ptr logoDrawable; juce::Component::SafePointer presetMenuSafePtr; bool inputFocused = false; float mapFrequencyToX(float freq, float minFreq, float maxFreq, float width); int getClosestBinForFrequency(float freq); void drawFrequencyGrid(juce::Graphics &g, const float dbRange); float getInterpolatedDb(float exactBin); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CrystalizerEQAudioProcessorEditor) };