├── Source ├── GUIComponents │ ├── .DS_Store │ ├── CustomLookAndFeel.h │ ├── CustomSlider.h │ ├── CustomSlider.cpp │ ├── FilterResponseDisplay.h │ ├── FilterResponseDisplay.cpp │ └── CustomLookAndFeel.cpp ├── AudioDSPComponents │ ├── .DS_Store │ ├── AudioFilter.cpp │ ├── VAOnePoleFilter.h │ ├── AudioFilter.h │ └── VAOnePoleFilter.cpp ├── PluginEditor.h ├── Parameters │ ├── CustomAudioParameter.h │ └── CustomAudioParameter.cpp ├── PluginProcessor.h ├── PluginEditor.cpp └── PluginProcessor.cpp ├── Builds └── MacOSX │ ├── RecentFilesMenuTemplate.nib │ └── Info.plist ├── JuceLibraryCode ├── modules │ ├── juce_core │ │ └── juce_core.h │ ├── juce_events │ │ └── juce_events.h │ ├── juce_opengl │ │ └── juce_opengl.h │ ├── juce_video │ │ └── juce_video.h │ ├── juce_graphics │ │ └── juce_graphics.h │ ├── juce_gui_extra │ │ └── juce_gui_extra.h │ ├── juce_gui_basics │ │ └── juce_gui_basics.h │ ├── juce_audio_basics │ │ └── juce_audio_basics.h │ ├── juce_cryptography │ │ └── juce_cryptography.h │ ├── juce_audio_devices │ │ └── juce_audio_devices.h │ ├── juce_audio_formats │ │ └── juce_audio_formats.h │ ├── juce_audio_processors │ │ └── juce_audio_processors.h │ ├── juce_data_structures │ │ └── juce_data_structures.h │ └── juce_audio_plugin_client │ │ └── juce_audio_plugin_client.h ├── ReadMe.txt ├── JuceHeader.h └── AppConfig.h ├── README.md └── FilterGuiDemo.jucer /Source/GUIComponents/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JoshMarler/filter-gui-demo/HEAD/Source/GUIComponents/.DS_Store -------------------------------------------------------------------------------- /Source/AudioDSPComponents/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JoshMarler/filter-gui-demo/HEAD/Source/AudioDSPComponents/.DS_Store -------------------------------------------------------------------------------- /Builds/MacOSX/RecentFilesMenuTemplate.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JoshMarler/filter-gui-demo/HEAD/Builds/MacOSX/RecentFilesMenuTemplate.nib -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_core/juce_core.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_core/juce_core.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_events/juce_events.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_events/juce_events.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_opengl/juce_opengl.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_opengl/juce_opengl.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_video/juce_video.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_video/juce_video.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_graphics/juce_graphics.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_graphics/juce_graphics.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_gui_extra/juce_gui_extra.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_gui_basics/juce_gui_basics.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_audio_basics/juce_audio_basics.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_cryptography/juce_cryptography.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_cryptography/juce_cryptography.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_audio_devices/juce_audio_devices.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_audio_formats/juce_audio_formats.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_audio_processors/juce_audio_processors.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_data_structures/juce_data_structures.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/modules/juce_audio_plugin_client/juce_audio_plugin_client.h: -------------------------------------------------------------------------------- 1 | // This is an auto-generated file to redirect any included 2 | // module headers to the correct external folder. 3 | 4 | #include "../../../../../SDK/JUCE/modules/juce_audio_plugin_client/juce_audio_plugin_client.h" 5 | -------------------------------------------------------------------------------- /JuceLibraryCode/ReadMe.txt: -------------------------------------------------------------------------------- 1 | 2 | Important Note!! 3 | ================ 4 | 5 | The purpose of this folder is to contain files that are auto-generated by the Introjucer, 6 | and ALL files in this folder will be mercilessly DELETED and completely re-written whenever 7 | the Introjucer saves your project. 8 | 9 | Therefore, it's a bad idea to make any manual changes to the files in here, or to 10 | put any of your own files in here if you don't want to lose them. (Of course you may choose 11 | to add the folder's contents to your version-control system so that you can re-merge your own 12 | modifications after the Introjucer has saved its changes). 13 | -------------------------------------------------------------------------------- /Source/GUIComponents/CustomLookAndFeel.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | CustomLookAndFeel.h 5 | Created: 19 Sep 2014 7:23:32pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #ifndef CustomLookAndFeel_H_INCLUDED 12 | #define CustomLookAndFeel_H_INCLUDED 13 | 14 | #include "../JuceLibraryCode/JuceHeader.h" 15 | 16 | 17 | class CustomLookAndFeel : public LookAndFeel_V3 18 | 19 | { 20 | public: 21 | void drawRotarySlider (Graphics&, int x, int y, int width, int height, 22 | float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, 23 | Slider&) override; 24 | }; 25 | 26 | 27 | 28 | #endif // CustomLookAndFeel_H_INCLUDED 29 | -------------------------------------------------------------------------------- /Source/GUIComponents/CustomSlider.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | ParameterSlider.h 5 | Created: 17 Feb 2016 9:58:49pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | #include "../JuceLibraryCode/JuceHeader.h" 11 | 12 | #ifndef CUSTOMSLIDER_H_INCLUDED 13 | #define CUSTOMSLIDER_H_INCLUDED 14 | 15 | class CustomSlider : public Slider, 16 | private Timer 17 | { 18 | public: 19 | CustomSlider(AudioProcessorParameter& p); 20 | 21 | void valueChanged() override; 22 | void timerCallback() override; 23 | 24 | void startedDragging() override; 25 | void stoppedDragging() override; 26 | 27 | double getValueFromText (const String& text) override; 28 | String getTextFromValue (double value) override; 29 | 30 | void updateSliderPos(); 31 | 32 | private: 33 | 34 | AudioProcessorParameter& param; 35 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomSlider) 36 | }; 37 | 38 | 39 | 40 | #endif // CUSTOMSLIDER_H_INCLUDED 41 | -------------------------------------------------------------------------------- /Source/GUIComponents/CustomSlider.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | ParameterSlider.cpp 5 | Created: 17 Feb 2016 9:58:49pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "CustomSlider.h" 12 | 13 | CustomSlider::CustomSlider (AudioProcessorParameter& p) 14 | : Slider (p.getName (256)), param (p) 15 | { 16 | startTimerHz (30); 17 | updateSliderPos(); 18 | } 19 | 20 | void CustomSlider::valueChanged() 21 | { 22 | 23 | float value = (float) Slider::getValue(); 24 | param.setValueNotifyingHost(value); 25 | } 26 | 27 | void CustomSlider::timerCallback() 28 | { 29 | updateSliderPos(); 30 | } 31 | 32 | void CustomSlider::startedDragging() 33 | { 34 | param.beginChangeGesture(); 35 | } 36 | void CustomSlider::stoppedDragging() 37 | { 38 | param.endChangeGesture(); 39 | } 40 | 41 | double CustomSlider::getValueFromText (const String& text) 42 | { 43 | return param.getValueForText (text); 44 | } 45 | 46 | String CustomSlider::getTextFromValue (double value) 47 | { 48 | 49 | return param.getText ((float) value, 1024); 50 | } 51 | 52 | void CustomSlider::updateSliderPos() 53 | { 54 | float newValue = param.getValue(); 55 | 56 | if (newValue != (float) Slider::getValue() && ! isMouseButtonDown()) 57 | Slider::setValue (newValue); 58 | } -------------------------------------------------------------------------------- /Source/AudioDSPComponents/AudioFilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | AudioFilter.cpp 5 | Created: 25 Jul 2015 10:05:58am 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "AudioFilter.h" 12 | 13 | AudioFilter::AudioFilter() 14 | { 15 | filterType = LowPass; 16 | } 17 | 18 | AudioFilter::~AudioFilter() 19 | { 20 | 21 | } 22 | 23 | void AudioFilter::setCutoff(float newCutoffFrequency) 24 | { 25 | cutoffFrequency = newCutoffFrequency; 26 | } 27 | 28 | 29 | float AudioFilter::getCutoff() const 30 | { 31 | return cutoffFrequency; 32 | } 33 | 34 | void AudioFilter::setGain(float newGain) 35 | { 36 | filterGain = newGain; 37 | } 38 | 39 | float AudioFilter::getGain() const 40 | { 41 | return filterGain; 42 | } 43 | 44 | void AudioFilter::setQFactor(float newQFactor) 45 | { 46 | qFactor = newQFactor; 47 | } 48 | 49 | float AudioFilter::getQFactor() const 50 | { 51 | return qFactor; 52 | } 53 | 54 | void AudioFilter::setFilterType(int type) 55 | { 56 | filterType = type; 57 | } 58 | 59 | int AudioFilter::getFilterType() const 60 | { 61 | return filterType; 62 | } 63 | 64 | //Default filter response implementations 65 | float AudioFilter::getMagnitudeResponse(float freq) const 66 | { 67 | return 0.0; 68 | } 69 | 70 | float AudioFilter::getPhaseResponse(float freq) const 71 | { 72 | return 0.0; 73 | } 74 | -------------------------------------------------------------------------------- /Builds/MacOSX/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CFBundleExecutable 7 | ${EXECUTABLE_NAME} 8 | CFBundleIconFile 9 | 10 | CFBundleIdentifier 11 | com.RudeAudio.FilterGuiDemo 12 | CFBundleName 13 | FilterGuiDemo 14 | CFBundlePackageType 15 | TDMw 16 | CFBundleSignature 17 | PTul 18 | CFBundleShortVersionString 19 | 1.0.0 20 | CFBundleVersion 21 | 1.0.0 22 | NSHumanReadableCopyright 23 | RudeAudio 24 | NSHighResolutionCapable 25 | 26 | AudioComponents 27 | 28 | 29 | name 30 | RudeAudio: FilterGuiDemo 31 | description 32 | FilterGuiDemo 33 | factoryFunction 34 | FilterGuiDemoAUFactory 35 | manufacturer 36 | RUDE 37 | type 38 | aufx 39 | subtype 40 | RFil 41 | version 42 | 65536 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FilterGuiDemo 2 | This plugin is an example of a simple filter frequency response display. 3 | The filter response GUI component is drawn using JUCE path's and is an evaluation of the filters transfer function. 4 | 5 | The code is basic and could do with some improvements. At present the filter in this plugin is a simple one pole 6 | virtual analogue filter so as to make the code dealing with the drawing of the filter response path as simple as 7 | possible to understand. 8 | 9 | Feel free to make any suggestions to this repo/code, I am hoping this plugin will be useful for JUCE newbies and give a 10 | starting point for a handy GUI class for others. I will gradually attempt to make the filter response GUI component/class more 11 | reusable and generic for inclusion into people's projects. 12 | 13 | For example the FilterReponseDisplay class could be setup to use a callback / std::function object to calculate the filters magnitude response rather than depending on an AudioFilter object. The function object would simply need to be passed into the FilterResponseDisplay class constructor. This could make the class a little more generic, I’ll leave it up to the reader for now and will add this in on a future version. 14 | 15 | Future revisions of this example plugin will implement further filter types (Moog Ladder, SVF etc.) and response shapes along with oversampling and some other goodies. 16 | 17 | 18 | 19 | 20 | Thanks and credit to the following for allowing me to use some of their code/Ideas to build off. 21 | 22 | The guys at Semantic Audio for letting me use/pull apart their own filter display class - http://www.semanticaudio.co.uk 23 | 24 | Vadim Zavalishin for the excellent VA Filter paper - https://www.native-instruments.com/fileadmin/ni_media/downloads/pdf/VAFilterDesign_1.1.1.pdf 25 | 26 | Cheers 27 | 28 | Josh -------------------------------------------------------------------------------- /JuceLibraryCode/JuceHeader.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | This is the header file that your files should include in order to get all the 7 | JUCE library headers. You should avoid including the JUCE headers directly in 8 | your own source files, because that wouldn't pick up the correct configuration 9 | options for your app. 10 | 11 | */ 12 | 13 | #ifndef __APPHEADERFILE_AOG7SJ__ 14 | #define __APPHEADERFILE_AOG7SJ__ 15 | 16 | #include "AppConfig.h" 17 | #include "modules/juce_audio_basics/juce_audio_basics.h" 18 | #include "modules/juce_audio_devices/juce_audio_devices.h" 19 | #include "modules/juce_audio_formats/juce_audio_formats.h" 20 | #include "modules/juce_audio_plugin_client/juce_audio_plugin_client.h" 21 | #include "modules/juce_audio_processors/juce_audio_processors.h" 22 | #include "modules/juce_core/juce_core.h" 23 | #include "modules/juce_cryptography/juce_cryptography.h" 24 | #include "modules/juce_data_structures/juce_data_structures.h" 25 | #include "modules/juce_events/juce_events.h" 26 | #include "modules/juce_graphics/juce_graphics.h" 27 | #include "modules/juce_gui_basics/juce_gui_basics.h" 28 | #include "modules/juce_gui_extra/juce_gui_extra.h" 29 | #include "modules/juce_opengl/juce_opengl.h" 30 | #include "modules/juce_video/juce_video.h" 31 | 32 | #if ! DONT_SET_USING_JUCE_NAMESPACE 33 | // If your code uses a lot of JUCE classes, then this will obviously save you 34 | // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE. 35 | using namespace juce; 36 | #endif 37 | 38 | #if ! JUCE_DONT_DECLARE_PROJECTINFO 39 | namespace ProjectInfo 40 | { 41 | const char* const projectName = "FilterGuiDemo"; 42 | const char* const versionString = "1.0.0"; 43 | const int versionNumber = 0x10000; 44 | } 45 | #endif 46 | 47 | #endif // __APPHEADERFILE_AOG7SJ__ 48 | -------------------------------------------------------------------------------- /Source/AudioDSPComponents/VAOnePoleFilter.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | VAOnePoleFilter.h 5 | Created: 9 Jul 2015 8:20:19pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #ifndef VAONEPOLEFILTER_H_INCLUDED 12 | #define VAONEPOLEFILTER_H_INCLUDED 13 | 14 | #include "JuceHeader.h" 15 | #include "AudioFilter.h" 16 | 17 | 18 | /* 19 | A very basic Virtual Analogue One Pole Filter based on calculations from The Art of VA Filter Design 20 | by Vadim Zavalishin see: 21 | 22 | NOTE : Need to implement oversampling as frequency response is incorrect at high frequencies approaching nyquist 23 | there is lost energy/gain which will be apparent on the filters response display (common issue with VA filters). You 24 | will notice the frequency response suddenly drops off as the cutoff approaches nyquist. 25 | This will be addressed in the next version. 26 | 27 | */ 28 | 29 | class VAOnePoleFilter : public AudioFilter 30 | { 31 | 32 | public: 33 | VAOnePoleFilter(); 34 | 35 | virtual ~VAOnePoleFilter(); 36 | 37 | // Must be called before playback starts to set sample rate etc. 38 | void initializeFilter(float initSampRate, float initMinFrequency, float initMaxFrequency) override; 39 | 40 | void setCutoff(float cutoff) override; 41 | 42 | //Overriding base class filter response functions for VA One Pole response 43 | float getMagnitudeResponse(float freq) const override; 44 | float processFilter(float input, int channel) override; 45 | 46 | private: 47 | // z1 delay element/state holder (performing analgue intergration equivalent) 48 | // set as a stereo option for now, possibly a better way of setting up / handling 49 | // stereo input between filter and plugin processor code but this is nice and simple. 50 | float z1 [2]; 51 | 52 | // Big G value for billinear cutoff/pre-warping 53 | float G = 0.0; 54 | }; 55 | 56 | #endif // VAONEPOLEFILTER_H_INCLUDED -------------------------------------------------------------------------------- /Source/PluginEditor.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated by the Introjucer! 5 | 6 | It contains the basic framework code for a JUCE plugin editor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #ifndef PLUGINEDITOR_H_INCLUDED 12 | #define PLUGINEDITOR_H_INCLUDED 13 | 14 | #include "../JuceLibraryCode/JuceHeader.h" 15 | #include "PluginProcessor.h" 16 | #include "GUIComponents/CustomLookAndFeel.h" 17 | #include "GUIComponents/FilterResponseDisplay.h" 18 | #include "GUIComponents/CustomSlider.h" 19 | 20 | 21 | //============================================================================== 22 | /** 23 | */ 24 | class FilterGuiDemoAudioProcessorEditor : public AudioProcessorEditor, 25 | public ComboBoxListener 26 | { 27 | public: 28 | 29 | FilterGuiDemoAudioProcessorEditor (FilterGuiDemoAudioProcessor&); 30 | ~FilterGuiDemoAudioProcessorEditor(); 31 | //============================================================================== 32 | void paint (Graphics&) override; 33 | void resized() override; 34 | 35 | //============================================================================== 36 | AudioProcessorParameter* getParameterFromSlider(const Slider* slider) const; 37 | 38 | //============================================================================== 39 | void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override; 40 | 41 | private: 42 | // This reference is provided as a quick way for your editor to 43 | // access the processor object that created it. 44 | FilterGuiDemoAudioProcessor& processor; 45 | 46 | //GUI Components 47 | CustomLookAndFeel customLookAndFeel; 48 | FilterResponseDisplay filterResponseDisplay; 49 | std::unique_ptr frequencyCutoffSlider, filterGainSlider; 50 | ComboBox filterTypeDropDown; 51 | Label filterCutoffLabel, filterGainLabel; 52 | 53 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterGuiDemoAudioProcessorEditor) 54 | }; 55 | 56 | 57 | #endif // PLUGINEDITOR_H_INCLUDED 58 | -------------------------------------------------------------------------------- /Source/GUIComponents/FilterResponseDisplay.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | FilterResponseDisplay.h 5 | Created: 20 Jul 2015 3:22:56pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #ifndef FILTERRESPONSEDISPLAY_H_INCLUDED 12 | #define FILTERRESPONSEDISPLAY_H_INCLUDED 13 | 14 | #include "JuceHeader.h" 15 | #include "../AudioDSPComponents/AudioFilter.h" 16 | 17 | // A filter magnitude response display class 18 | // modified from and based on some code by semanticaudio 19 | // see https://github.com/semanticaudio/SAFE 20 | // Check the SAFE FilterGraph class for reference. 21 | 22 | class FilterResponseDisplay : public Component, 23 | public SliderListener 24 | { 25 | public: 26 | FilterResponseDisplay(const AudioFilter& filter); 27 | ~FilterResponseDisplay(); 28 | 29 | //Function to update the filter being used for the response display incase of change in processor 30 | void setFilterToUse(const AudioFilter& filter); 31 | 32 | void paint(Graphics& g) override; 33 | 34 | //Filter response type drawing functions 35 | void drawLowpass(); 36 | void drawHighpass(); 37 | 38 | /* 39 | Setup as a slider listener so that filter display can be updated/call it's paint method when the relevant 40 | filter cutoff slider is moved. 41 | 42 | There are various possible ways to handle updating the display when a change in the filter occurs but this 43 | seems the simplest. 44 | */ 45 | void sliderValueChanged (Slider* slider) override; 46 | 47 | //Sets the colour of the filters response curve 48 | void setMagResponseColour(Colour newColour); 49 | 50 | void setDisplayBackgroundColour(Colour newColour); 51 | 52 | float xAxisToFrequency (float xPos); 53 | float dbToYAxis(float db); 54 | 55 | void setMaxDecibels(float maxDB); 56 | 57 | private: 58 | //The AudioFilterResponse object the display will use to draw the magnitude response curve 59 | const AudioFilter* filterToUse; 60 | 61 | //Drawing / Graphics members 62 | Path magnitudeResponsePath; 63 | float filterPathThickness = 4.0; 64 | Colour magResponseColour; 65 | Colour displayBackgroundColor; 66 | 67 | float minFrequency = 0.0; 68 | float maxFrequency = 0.0; 69 | float maxDecibels = 0.0; 70 | 71 | }; 72 | 73 | #endif // FILTERRESPONSEDISPLAY_H_INCLUDED 74 | -------------------------------------------------------------------------------- /Source/Parameters/CustomAudioParameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | CustomAudioParameter.h 5 | Created: 19 Jun 2015 7:27:04pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #ifndef CustomAudioParameter_H_INCLUDED 12 | #define CustomAudioParameter_H_INCLUDED 13 | 14 | #include "../JuceLibraryCode/JuceHeader.h" 15 | 16 | /* 17 | Modified parameter class with some basic added extra's like ability to specify a callback funtion using std::function to be 18 | called whenever parameter value changes. This class uses a NormalisableRange range object which defaults to 0.0 - 1.0 range unless 19 | otherwise specified using the setNormalisableRange function. 20 | 21 | The value passed into setValue should still be between 0.0 and 1.0 - the NormalisableRange object is NOT used to convert values outside 22 | of a 0.0 - 1.0 range passed to setValue. The NormalisableRange converts 0.0 - 1.0 values to a custom range specified by the user which 23 | can then be used for display purposes and also passed to the setValueCallback function. 24 | */ 25 | class CustomAudioParameter : public AudioProcessorParameterWithID 26 | { 27 | public: 28 | 29 | CustomAudioParameter(String initParameterID, String initParameterName, bool initUseNormalizedForCallback); 30 | 31 | //initSetValueCallback is a callback function you can specify (using a lambda etc) that will be called every time the parameter value changes. 32 | CustomAudioParameter(String initParameterID, String initParameterName, bool initUseNormalizedForCallback, std::function initSetValueCallback); 33 | 34 | ~CustomAudioParameter(); 35 | 36 | float getValue() const override; 37 | void setValue(float newValue) override; 38 | 39 | void setNormalisableRange(NormalisableRange newRange); 40 | void setNormalisableRange(float start, float end); 41 | 42 | float getDefaultValue() const override; 43 | String getName(int maximumStringLength) const override; 44 | 45 | void setLabel(String newLabelText); 46 | String getLabel() const override; 47 | 48 | float getValueForText (const String& text) const override; 49 | String getText(float value, int = 0 /*Maximum String Length - default value provided/optional*/) const override; 50 | 51 | private: 52 | 53 | float defaultValue = 0.5; 54 | NormalisableRange range; 55 | 56 | //Using std::atomic for the host/GUI changeable value to stop any data race conditions etc. As per Timur's talk. 57 | //The gui thread will be updating this value via the ParameterSlider objects etc. 58 | std::atomic normalisedValue; 59 | std::atomic unnormalisedValue; 60 | 61 | bool useNormalizedForCallback; 62 | 63 | String name; 64 | String labelText = ""; 65 | 66 | std::function setValueCallback = nullptr; 67 | }; 68 | 69 | 70 | 71 | 72 | #endif // CustomAudioParameter_H_INCLUDED 73 | -------------------------------------------------------------------------------- /Source/Parameters/CustomAudioParameter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | CustomAudioParameter.cpp 5 | Created: 19 Jun 2015 7:27:04pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "CustomAudioParameter.h" 12 | 13 | CustomAudioParameter::CustomAudioParameter(String initParameterID, String initParameterName, bool initUseNormalizedForCallback) : AudioProcessorParameterWithID(initParameterID, initParameterName), normalisedValue(0.0), unnormalisedValue(0.0) 14 | { 15 | name = initParameterName; 16 | useNormalizedForCallback = initUseNormalizedForCallback; 17 | range.start = 0.0; 18 | range.end = 1.0; 19 | } 20 | 21 | 22 | CustomAudioParameter::CustomAudioParameter(String initParameterID, String initParameterName, bool initUseNormalizedForCallback, std::function initSetValueCallback) : AudioProcessorParameterWithID(initParameterID, initParameterName), normalisedValue(0.0), unnormalisedValue(0.0) 23 | { 24 | name = initParameterName; 25 | useNormalizedForCallback = initUseNormalizedForCallback; 26 | setValueCallback = initSetValueCallback; 27 | range.start = 0.0; 28 | range.end = 1.0; 29 | } 30 | 31 | 32 | CustomAudioParameter::~CustomAudioParameter() 33 | { 34 | 35 | } 36 | 37 | 38 | float CustomAudioParameter::getValue() const 39 | { 40 | return normalisedValue.load(); 41 | } 42 | 43 | 44 | void CustomAudioParameter::setValue (float newValue) 45 | { 46 | normalisedValue.store(newValue); 47 | unnormalisedValue.store(range.convertFrom0to1(newValue)); 48 | 49 | if (setValueCallback != nullptr) 50 | { 51 | if (useNormalizedForCallback) 52 | { 53 | setValueCallback(normalisedValue.load()); 54 | } 55 | else 56 | { 57 | setValueCallback(unnormalisedValue.load()); 58 | } 59 | } 60 | } 61 | 62 | void CustomAudioParameter::setNormalisableRange(NormalisableRange newRange) 63 | { 64 | range = newRange; 65 | } 66 | 67 | void CustomAudioParameter::setNormalisableRange(float start, float end) 68 | { 69 | range.start = start; 70 | range.end = end; 71 | } 72 | 73 | float CustomAudioParameter::getDefaultValue() const 74 | { 75 | return defaultValue; 76 | } 77 | 78 | 79 | String CustomAudioParameter::getName(int maximumStringLength) const 80 | { 81 | return name; 82 | } 83 | 84 | void CustomAudioParameter::setLabel(String newLabelText) 85 | { 86 | labelText = newLabelText; 87 | } 88 | 89 | String CustomAudioParameter::getLabel() const 90 | { 91 | return labelText; 92 | } 93 | 94 | 95 | float CustomAudioParameter::getValueForText(const String& text) const 96 | { 97 | return range.convertFrom0to1(text.getFloatValue()); 98 | } 99 | 100 | 101 | String CustomAudioParameter::getText(float value, int/*Maximum String Length - default value provided/optional*/) const 102 | { 103 | String text = String(range.convertFrom0to1(value)) + getLabel(); 104 | return text; 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /Source/AudioDSPComponents/AudioFilter.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | AudioFilter.h 5 | Created: 25 Jul 2015 10:05:58am 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #ifndef AUDIOFILTER_H_INCLUDED 12 | #define AUDIOFILTER_H_INCLUDED 13 | 14 | #include "JuceHeader.h" 15 | 16 | //Abstract audio filter base class 17 | class AudioFilter 18 | { 19 | public: 20 | 21 | AudioFilter(); 22 | 23 | virtual ~AudioFilter(); 24 | 25 | //Filters deriving from this class must implement their process method 26 | virtual float processFilter (float input, int channel) = 0; 27 | 28 | /* 29 | Filters deriving from this class must implement their own initialization code - i.e for flushing storage registers/state holders etc. 30 | Must be called before playback starts to set sample rate etc. 31 | */ 32 | virtual void initializeFilter (float initSampleRate, float initMinFrequency, float initMaxFrequency) = 0; 33 | 34 | inline void setSampleRate (float newSampRate) {sampleRate = newSampRate;} 35 | inline float getSampleRate() const {return sampleRate;} 36 | 37 | inline void setMinFrequency (float newMinFrequency) {minFrequency = newMinFrequency;} 38 | inline float getMinFrequency() const {return minFrequency;} 39 | 40 | inline void setMaxFrequency (float newMaxFrequency) {maxFrequency = newMaxFrequency;} 41 | inline float getMaxFrequency() const {return maxFrequency;} 42 | 43 | 44 | /* 45 | Parameter set and get methods made virtual to facilitate use in different types of filters - i.e virtual analogue filters where 46 | cutoff frequencies may need to be pre-warped etc. 47 | */ 48 | virtual void setCutoff (float newCutoff); 49 | virtual float getCutoff() const; 50 | virtual void setQFactor (float newQFactor); 51 | virtual float getQFactor() const; 52 | virtual void setGain (float newGain); 53 | virtual float getGain() const; 54 | 55 | /* 56 | Filter response functions that can be overriden in baseclass to return the filters magnitude and phase response. 57 | These can be used to provide frequency response for displays and GUI components etc. Default implementations just return 0.0. 58 | These functions do not HAVE to be implemented - the filter object will run fine without them. 59 | */ 60 | virtual float getMagnitudeResponse(float freq) const; 61 | virtual float getPhaseResponse(float freq) const; 62 | 63 | void setFilterType (int newFilterType); 64 | int getFilterType() const; 65 | 66 | //Filter types / response shapes - lowpass, highpass etc. 67 | enum filterTypeList 68 | { 69 | LowPass = 0, 70 | HighPass 71 | }; 72 | 73 | protected: 74 | 75 | float sampleRate = 0.0; 76 | float minFrequency = 0.0; 77 | float maxFrequency = 0.0; 78 | 79 | //Wrap these as std::atomic ? What if they are updated by lfo etc and the gui thread also reads them in some way through magnitudeResponse call etc. 80 | float cutoffFrequency = 0.0; 81 | float filterGain = 1.0; 82 | float qFactor = 0.0; 83 | 84 | int filterType = 0; 85 | }; 86 | 87 | #endif // AUDIOFILTER_H_INCLUDED 88 | -------------------------------------------------------------------------------- /Source/PluginProcessor.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated by the Introjucer! 5 | 6 | It contains the basic framework code for a JUCE plugin processor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #ifndef PLUGINPROCESSOR_H_INCLUDED 12 | #define PLUGINPROCESSOR_H_INCLUDED 13 | 14 | #include "../JuceLibraryCode/JuceHeader.h" 15 | #include "AudioDSPComponents/VAOnePoleFilter.h" 16 | #include "Parameters/CustomAudioParameter.h" 17 | 18 | 19 | //============================================================================== 20 | /** 21 | */ 22 | class FilterGuiDemoAudioProcessor : public AudioProcessor 23 | { 24 | public: 25 | //============================================================================== 26 | FilterGuiDemoAudioProcessor(); 27 | ~FilterGuiDemoAudioProcessor(); 28 | 29 | //============================================================================== 30 | void prepareToPlay (double sampleRate, int samplesPerBlock) override; 31 | void releaseResources() override; 32 | 33 | void processBlock (AudioSampleBuffer&, MidiBuffer&) override; 34 | 35 | //============================================================================== 36 | AudioProcessorEditor* createEditor() override; 37 | bool hasEditor() const override; 38 | 39 | //============================================================================== 40 | const String getName() const override; 41 | 42 | bool acceptsMidi() const override; 43 | bool producesMidi() const override; 44 | bool silenceInProducesSilenceOut() const override; 45 | double getTailLengthSeconds() const override; 46 | 47 | //============================================================================== 48 | int getNumPrograms() override; 49 | int getCurrentProgram() override; 50 | void setCurrentProgram (int index) override; 51 | const String getProgramName (int index) override; 52 | void changeProgramName (int index, const String& newName) override; 53 | 54 | //============================================================================== 55 | void getStateInformation (MemoryBlock& destData) override; 56 | void setStateInformation (const void* data, int sizeInBytes) override; 57 | 58 | //Returns the processors AudioFilter instance for GUI code etc. 59 | AudioFilter& getAudioFilter(); 60 | 61 | CustomAudioParameter* filterCutoffParam; 62 | CustomAudioParameter* filterGainParam; 63 | 64 | // these are used to persist the UI's size - the values are stored along with the 65 | // filter's other parameters, and the UI component will update them when it gets 66 | // resized. 67 | int lastUIWidth, lastUIHeight; 68 | 69 | private: 70 | //============================================================================== 71 | 72 | 73 | std::unique_ptr filter1; 74 | 75 | //CustomAudioParameter* filterCutoffParam; 76 | //CustomAudioParameter* filterGainParam; 77 | 78 | //Default setup values for filter etc. 79 | const float defaultSampleRate = 44100.00; 80 | const float defaultMinFilterFrequency = 20.00; 81 | const float defaultMaxFilterFrequency = 20000.00; 82 | 83 | //============================================================================== 84 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterGuiDemoAudioProcessor) 85 | }; 86 | 87 | 88 | #endif // PLUGINPROCESSOR_H_INCLUDED 89 | -------------------------------------------------------------------------------- /Source/AudioDSPComponents/VAOnePoleFilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | VAOnePoleFilter.cpp 5 | Created: 9 Jul 2015 8:20:19pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "VAOnePoleFilter.h" 12 | 13 | VAOnePoleFilter::VAOnePoleFilter() 14 | { 15 | 16 | } 17 | 18 | VAOnePoleFilter::~VAOnePoleFilter() 19 | { 20 | 21 | } 22 | 23 | //Set variables and flush state registers in initialization function. Must be called before playback starts 24 | void VAOnePoleFilter::initializeFilter(float initSampleRate, float initMinFrequency, float initMaxFrequency) 25 | { 26 | this->sampleRate = initSampleRate; 27 | this->minFrequency = initMinFrequency; 28 | this->maxFrequency = initMaxFrequency; 29 | 30 | //clear the delay elements/state holders 31 | z1[0] = 0.0; 32 | z1[1] = 0.0; 33 | } 34 | 35 | 36 | void VAOnePoleFilter::setCutoff(float newCutoff) 37 | { 38 | this->cutoffFrequency = newCutoff; 39 | 40 | //Cutoff prewarping, billinear transform filters - see Art Of VA Filter Design. 41 | float wd = 2 * M_PI * this->cutoffFrequency; 42 | float T = 1/this->sampleRate; 43 | 44 | /* Desired analogue frequency / these are virtual analogue filters so this is the cutoff / frequency response we require for out filter algorithm */ 45 | float wa = (2/T) * tan(wd*T/2); 46 | float g = wa * T/2; 47 | G = g/(1.0 + g); 48 | } 49 | 50 | 51 | float VAOnePoleFilter::processFilter(float input, int channel) 52 | { 53 | float output = 0.0; 54 | float v = (input - z1[channel]) * G; 55 | float lowpassOutput = v + z1[channel]; 56 | 57 | //Update the z1 delay / state holder with new filter output for use in next sample 58 | z1 [channel]= lowpassOutput + v; 59 | 60 | float highpassOutput = input - lowpassOutput; 61 | 62 | switch (this->filterType) { 63 | case AudioFilter::filterTypeList::LowPass: 64 | output = this->filterGain * lowpassOutput; 65 | break; 66 | case AudioFilter::filterTypeList::HighPass: 67 | output = this->filterGain * highpassOutput; 68 | break; 69 | default: 70 | break; 71 | } 72 | 73 | return output; 74 | } 75 | 76 | float VAOnePoleFilter::getMagnitudeResponse(float frequency) const 77 | { 78 | float magnitude = 0.0; 79 | float T = 1/this->sampleRate; 80 | 81 | float wdCutoff = 2 * M_PI * this->cutoffFrequency; 82 | 83 | //Calculating pre-warped/analogue cutoff frequency to use in virtual analogue frequeny response calculations 84 | float cutOff = (2/T) * tan(wdCutoff*T/2); 85 | 86 | //Digital frequency to evaluate 87 | float wdEval = 2 * M_PI * frequency; 88 | float sValue = (2/T) * tan(wdEval*T/2); 89 | 90 | 91 | /* This is the digital transfer function which is equal to the analogue transfer function 92 | evaluated at H(s) where s = (2/T) * tan(wd*T/2) hence why the cutoff used is the pre warped analogue equivalent. 93 | See Art Of VA Filter Design 3.8 Bilinear Transform Section */ 94 | switch (this->filterType) { 95 | case AudioFilter::filterTypeList::LowPass: 96 | //VA Lowpass Frequency response wc/s+wc 97 | magnitude = cutOff/(sValue + cutOff); 98 | break; 99 | case AudioFilter::filterTypeList::HighPass: 100 | //VA Highpass Frequency response s/s+wc 101 | magnitude = sValue/(sValue + cutOff); 102 | break; 103 | default: 104 | break; 105 | } 106 | 107 | magnitude = magnitude * this->filterGain; 108 | 109 | //Convert to db for log db response display 110 | magnitude = Decibels::gainToDecibels(magnitude); 111 | return magnitude; 112 | } 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /Source/PluginEditor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated by the Introjucer! 5 | 6 | It contains the basic framework code for a JUCE plugin editor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "PluginProcessor.h" 12 | #include "PluginEditor.h" 13 | 14 | 15 | //============================================================================== 16 | FilterGuiDemoAudioProcessorEditor::FilterGuiDemoAudioProcessorEditor (FilterGuiDemoAudioProcessor& p) 17 | : AudioProcessorEditor (&p), processor (p), customLookAndFeel(), 18 | filterResponseDisplay(processor.getAudioFilter()), frequencyCutoffSlider(new CustomSlider (*processor.filterCutoffParam)),filterGainSlider(new CustomSlider (*processor.filterGainParam)) 19 | { 20 | // Make sure that before the constructor has finished, you've set the 21 | // editor's size to whatever you need it to be. 22 | setSize (600, 500); 23 | 24 | FilterGuiDemoAudioProcessorEditor::setLookAndFeel(&customLookAndFeel); 25 | 26 | //Setup Components 27 | addAndMakeVisible(*frequencyCutoffSlider); 28 | frequencyCutoffSlider->setName("FilterCutoff"); 29 | frequencyCutoffSlider->setSliderStyle(Slider::Rotary); 30 | frequencyCutoffSlider->setRange(0.0, 1.0); 31 | 32 | //Logarithmic frequency - 0.5 skew factor effectivley makes this a volt-octave knob. 33 | frequencyCutoffSlider->setSkewFactor(0.5); 34 | frequencyCutoffSlider->setColour(Slider::rotarySliderOutlineColourId, Colours::greenyellow); 35 | frequencyCutoffSlider->setColour(Slider::rotarySliderFillColourId, Colours::greenyellow); 36 | 37 | addAndMakeVisible(*filterGainSlider); 38 | filterGainSlider->setName("FilterGain"); 39 | filterGainSlider->setSliderStyle(Slider::Rotary); 40 | filterGainSlider->setRange(0.0, 1.0, 0.05); 41 | filterGainSlider->setColour(Slider::rotarySliderOutlineColourId, Colours::greenyellow); 42 | filterGainSlider->setColour(Slider::rotarySliderFillColourId, Colours::greenyellow); 43 | 44 | addAndMakeVisible(filterCutoffLabel); 45 | filterCutoffLabel.setText("Cutoff", juce::NotificationType::dontSendNotification); 46 | filterCutoffLabel.setFont(Font ("Cracked", 27.50f, Font::plain)); 47 | filterCutoffLabel.setColour(Label::textColourId, Colours::greenyellow); 48 | 49 | addAndMakeVisible(filterGainLabel); 50 | filterGainLabel.setText("Gain", juce::NotificationType::dontSendNotification); 51 | filterGainLabel.setFont(Font ("Cracked", 27.50f, Font::plain)); 52 | filterGainLabel.setColour(Label::textColourId, Colours::greenyellow); 53 | 54 | addAndMakeVisible(filterTypeDropDown); 55 | filterTypeDropDown.addItem("LowPass", 1); 56 | filterTypeDropDown.addItem("HighPass", 2); 57 | filterTypeDropDown.setColour(ComboBox::outlineColourId, Colours::greenyellow); 58 | filterTypeDropDown.setColour(ComboBox::backgroundColourId, Colours::black); 59 | filterTypeDropDown.setColour(ComboBox::textColourId, Colours::greenyellow); 60 | filterTypeDropDown.setColour(ComboBox::buttonColourId, Colours::greenyellow); 61 | filterTypeDropDown.setSelectedId(1); 62 | filterTypeDropDown.addListener(this); 63 | 64 | addAndMakeVisible(filterResponseDisplay); 65 | filterResponseDisplay.setMagResponseColour(Colours::greenyellow); 66 | filterResponseDisplay.setDisplayBackgroundColour(Colours::darkgrey); 67 | filterResponseDisplay.setBounds(50, 125, 500, 200); 68 | 69 | //Add the filter display as a listener to the cutoff and gain sliders for updates. 70 | frequencyCutoffSlider->addListener(&filterResponseDisplay); 71 | filterGainSlider->addListener(&filterResponseDisplay); 72 | } 73 | 74 | FilterGuiDemoAudioProcessorEditor::~FilterGuiDemoAudioProcessorEditor() 75 | { 76 | } 77 | 78 | 79 | //============================================================================== 80 | void FilterGuiDemoAudioProcessorEditor::comboBoxChanged(ComboBox* comboBoxThatChanged) 81 | { 82 | //In here set filter type and also update filter response display's filter type 83 | if (comboBoxThatChanged == &filterTypeDropDown) 84 | { 85 | int filterTypeIndex = filterTypeDropDown.getSelectedItemIndex(); 86 | processor.getAudioFilter().setFilterType(filterTypeIndex); 87 | 88 | //Repaint response display for filter type change 89 | filterResponseDisplay.repaint(); 90 | } 91 | } 92 | 93 | //============================================================================== 94 | void FilterGuiDemoAudioProcessorEditor::paint (Graphics& g) 95 | { 96 | g.fillAll (Colours::black); 97 | } 98 | 99 | void FilterGuiDemoAudioProcessorEditor::resized() 100 | { 101 | filterResponseDisplay.setBounds(50, 125, 500, 200); 102 | filterTypeDropDown.setBounds(225, 50, 150, 30); 103 | 104 | filterCutoffLabel.setBounds(85, 358, 130, 20); 105 | frequencyCutoffSlider->setBounds(50, 386, 135, 105); 106 | frequencyCutoffSlider->setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, true, 75, 20); 107 | 108 | filterGainLabel.setBounds(443, 358, 130, 20); 109 | filterGainSlider->setBounds(400, 386, 135, 105); 110 | filterGainSlider->setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, true, 75, 20); 111 | } 112 | -------------------------------------------------------------------------------- /Source/GUIComponents/FilterResponseDisplay.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | FilterResponseDisplay.cpp 5 | Created: 20 Jul 2015 3:22:56pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "FilterResponseDisplay.h" 12 | 13 | 14 | FilterResponseDisplay::FilterResponseDisplay(const AudioFilter& filter) 15 | { 16 | filterToUse = &filter; 17 | minFrequency = filterToUse->getMinFrequency(); 18 | maxFrequency = filterToUse->getMaxFrequency(); 19 | 20 | /* 21 | Sets the filter response displays min - max decibel range - Try changing this value to lower or higher decibel ranges 22 | to see the effect on the frequency response that is shown. If implementing a filter with resonant peaks you will need 23 | to ensure the peak values do not exceed the maxDecibels level. 24 | */ 25 | maxDecibels = 20.00; 26 | } 27 | 28 | 29 | FilterResponseDisplay::~FilterResponseDisplay() 30 | { 31 | 32 | } 33 | 34 | void FilterResponseDisplay::setFilterToUse(const AudioFilter& filter) 35 | { 36 | filterToUse = &filter; 37 | minFrequency = filterToUse->getMinFrequency(); 38 | maxFrequency = filterToUse->getMaxFrequency(); 39 | } 40 | 41 | void FilterResponseDisplay::paint(Graphics& g) 42 | { 43 | float width = (float) getWidth(); 44 | float height = (float) getHeight(); 45 | 46 | g.setColour(displayBackgroundColor); 47 | g.fillRect(0.0, 0.0, width, height); 48 | 49 | magnitudeResponsePath.clear(); 50 | 51 | int filterType = filterToUse->getFilterType(); 52 | switch (filterType) { 53 | case AudioFilter::filterTypeList::LowPass: 54 | drawLowpass(); 55 | break; 56 | case AudioFilter::filterTypeList::HighPass: 57 | drawHighpass(); 58 | break; 59 | } 60 | 61 | //Close the response path drawn 62 | magnitudeResponsePath.closeSubPath(); 63 | 64 | g.setColour(magResponseColour); 65 | g.strokePath(magnitudeResponsePath, PathStrokeType(filterPathThickness)); 66 | 67 | /* 68 | Fill area under/inside path with same colour at a lower alpha / highlight value. 69 | Try setting magResponseColour.withAlpha value to different values to get a fill shade you like. 70 | */ 71 | g.setColour(magResponseColour.withAlpha((uint8) 0x9a)); 72 | g.fillPath(magnitudeResponsePath); 73 | 74 | } 75 | 76 | //Draws filter display path for Lowpas response 77 | void FilterResponseDisplay::drawLowpass() 78 | { 79 | float freq = 0.0; 80 | float magnitudeDBValue = 0.0; 81 | 82 | /* 83 | LowPass so start path on left hand side of component i.e at 0.0f - using 0.0f - (filterPathThickness/2) for asthetic purposes 84 | Try commeting out - (filterPathThickness/2) to see the effect. This line hides the highlighted path edge so that the path edge 85 | highlight shows only on the top of the magnitude response path. 86 | */ 87 | magnitudeResponsePath.startNewSubPath((0.0f - (filterPathThickness/2)), (getBottom() - (filterPathThickness/2))); 88 | magnitudeDBValue = filterToUse->getMagnitudeResponse(minFrequency); 89 | magnitudeResponsePath.lineTo((0.0f - (filterPathThickness/2)) , dbToYAxis(magnitudeDBValue)); 90 | 91 | for (float xPos = 0.0; xPos < ((float) getWidth() + (filterPathThickness/2)); xPos += (filterPathThickness/2)) 92 | { 93 | //Get the frequency value for the filter's magnitude response calculation 94 | freq = xAxisToFrequency(xPos); 95 | magnitudeDBValue = filterToUse->getMagnitudeResponse(freq); 96 | magnitudeResponsePath.lineTo(xPos, dbToYAxis(magnitudeDBValue)); 97 | } 98 | 99 | magnitudeDBValue = filterToUse->getMagnitudeResponse(maxFrequency); 100 | magnitudeResponsePath.lineTo(((float) getWidth() + (filterPathThickness/2)), dbToYAxis(magnitudeDBValue)); 101 | 102 | /* 103 | Dirty Trick to close the path nicely when cutoff is at max level (this is not apparent for virtual analogue filters that have not been 104 | oversampled when cutoff is close to nyquist as the response is pulled to zero). Try commenting this line out and running the plugin at 105 | higher sample rate i.e 96khz to see the visual result of the path closing without this. 106 | */ 107 | magnitudeResponsePath.lineTo(((float) getWidth() + (filterPathThickness/2)), (getBottom() - (filterPathThickness/2))); 108 | 109 | } 110 | 111 | //Draws filter display path for Highpass response 112 | void FilterResponseDisplay::drawHighpass() 113 | { 114 | float freq = 0.0; 115 | float magnitudeDBValue = 0.0; 116 | 117 | //If HighPass start path on right hand side of component i.e at component width. 118 | magnitudeDBValue = filterToUse->getMagnitudeResponse(maxFrequency); 119 | magnitudeResponsePath.startNewSubPath(((float) getWidth() + (filterPathThickness/2)), (getBottom() - (filterPathThickness/2))); 120 | magnitudeResponsePath.lineTo(((float) getWidth() + (filterPathThickness/2)), dbToYAxis(magnitudeDBValue)); 121 | 122 | for (float xPos = ((float) getWidth()); xPos > (filterPathThickness/2); xPos -= (filterPathThickness/2)) 123 | { 124 | //Get the frequency value for the filter's magnitude response calculation 125 | freq = xAxisToFrequency(xPos); 126 | magnitudeDBValue = filterToUse->getMagnitudeResponse(freq); 127 | magnitudeResponsePath.lineTo(xPos, dbToYAxis(magnitudeDBValue)); 128 | } 129 | 130 | magnitudeDBValue = filterToUse->getMagnitudeResponse(minFrequency); 131 | magnitudeResponsePath.lineTo ((0.0f - (filterPathThickness/2)), dbToYAxis(magnitudeDBValue)); 132 | 133 | /* 134 | Dirty trick again to close the path nicely when cutoff at min level for High Pass - try cmmenting this line out to se the visual 135 | effect on the reponse path closing without it 136 | */ 137 | magnitudeResponsePath.lineTo ((0.0f - (filterPathThickness/2)), (getBottom() - (filterPathThickness/2))); 138 | } 139 | 140 | 141 | void FilterResponseDisplay::setMagResponseColour(Colour newColour) 142 | { 143 | magResponseColour = newColour; 144 | } 145 | 146 | 147 | void FilterResponseDisplay::setDisplayBackgroundColour(Colour newColour) 148 | { 149 | displayBackgroundColor = newColour; 150 | } 151 | 152 | //Converts position along the FilterReponseDisplay component's X-Axis into frequency for use in filter response calculation. 153 | float FilterResponseDisplay::xAxisToFrequency(float xPos) 154 | { 155 | float width = getWidth(); 156 | //Computes frequency from position on x axis of component. So if the xPos is equal to the component width the value returned will be maxFrequency. 157 | float frequency = minFrequency * pow((maxFrequency / minFrequency), (xPos / width)); 158 | return frequency; 159 | } 160 | 161 | 162 | float FilterResponseDisplay::dbToYAxis(float dbGain) 163 | { 164 | float height = getHeight(); 165 | 166 | //Scale gain with this value, height of component is divded by maxDB * 2 for -max to +max db response display 167 | float scale = - (height) / (maxDecibels * 2); 168 | float scaledDbGain = dbGain * scale; 169 | 170 | /* 171 | Negative db values will result in a negative yposition so add height/2 to result to scale into 172 | correct component position for drawing. Test these calculations with a dbGain value equal to maxDecibels and 173 | yPostion computed will be equal to the filterResponseDisplay's height as is correct. 174 | */ 175 | float yPosition = scaledDbGain + (height / 2); 176 | return yPosition; 177 | } 178 | 179 | void FilterResponseDisplay::setMaxDecibels(float maxDB) 180 | { 181 | maxDecibels = maxDB; 182 | } 183 | 184 | void FilterResponseDisplay::sliderValueChanged (Slider* slider) 185 | { 186 | this->repaint(); 187 | } 188 | -------------------------------------------------------------------------------- /Source/PluginProcessor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | This file was auto-generated by the Introjucer! 5 | 6 | It contains the basic framework code for a JUCE plugin processor. 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "PluginProcessor.h" 12 | #include "PluginEditor.h" 13 | 14 | 15 | //============================================================================== 16 | FilterGuiDemoAudioProcessor::FilterGuiDemoAudioProcessor() : filter1(new VAOnePoleFilter()) 17 | { 18 | /* The lambda is capturing a value copy of the this pointer to the audio processor. The processor will be destroyed after the parameter object so 19 | this is safe.*/ 20 | auto cutoffParamCallback = [this] (float newCutoff){this->filter1->setCutoff(newCutoff);}; 21 | filterCutoffParam = new CustomAudioParameter("FilterCutoff", "FilterCutoff", false, cutoffParamCallback); 22 | 23 | //This param represents a filters cutoff frequency so appending Hz string to label for display purposes. 24 | filterCutoffParam->setLabel("Hz"); 25 | 26 | /* 27 | The filters min and max frequency will be used as normalized range values and values in this range will be passed to the set value 28 | callback function. 29 | */ 30 | filterCutoffParam->setNormalisableRange(defaultMinFilterFrequency, defaultMaxFilterFrequency); 31 | 32 | auto gainParamCallback = [this] (float gain) {this->filter1->setGain(gain);}; 33 | filterGainParam = new CustomAudioParameter("FilterGain", "FilterGain", true, gainParamCallback); 34 | 35 | addParameter(filterCutoffParam); 36 | addParameter(filterGainParam); 37 | } 38 | 39 | FilterGuiDemoAudioProcessor::~FilterGuiDemoAudioProcessor() 40 | { 41 | } 42 | 43 | //============================================================================== 44 | const String FilterGuiDemoAudioProcessor::getName() const 45 | { 46 | return JucePlugin_Name; 47 | } 48 | 49 | bool FilterGuiDemoAudioProcessor::acceptsMidi() const 50 | { 51 | #if JucePlugin_WantsMidiInput 52 | return true; 53 | #else 54 | return false; 55 | #endif 56 | } 57 | 58 | bool FilterGuiDemoAudioProcessor::producesMidi() const 59 | { 60 | #if JucePlugin_ProducesMidiOutput 61 | return true; 62 | #else 63 | return false; 64 | #endif 65 | } 66 | 67 | bool FilterGuiDemoAudioProcessor::silenceInProducesSilenceOut() const 68 | { 69 | return false; 70 | } 71 | 72 | double FilterGuiDemoAudioProcessor::getTailLengthSeconds() const 73 | { 74 | return 0.0; 75 | } 76 | 77 | int FilterGuiDemoAudioProcessor::getNumPrograms() 78 | { 79 | return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, 80 | // so this should be at least 1, even if you're not really implementing programs. 81 | } 82 | 83 | int FilterGuiDemoAudioProcessor::getCurrentProgram() 84 | { 85 | return 0; 86 | } 87 | 88 | void FilterGuiDemoAudioProcessor::setCurrentProgram (int index) 89 | { 90 | } 91 | 92 | const String FilterGuiDemoAudioProcessor::getProgramName (int index) 93 | { 94 | return String(); 95 | } 96 | 97 | void FilterGuiDemoAudioProcessor::changeProgramName (int index, const String& newName) 98 | { 99 | } 100 | 101 | //============================================================================== 102 | void FilterGuiDemoAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) 103 | { 104 | // Use this method as the place to do any pre-playback 105 | // initialisation that you need.. 106 | 107 | //Initialize our audio filter for playback. 108 | filter1->initializeFilter(sampleRate, defaultMinFilterFrequency, defaultMaxFilterFrequency); 109 | } 110 | 111 | void FilterGuiDemoAudioProcessor::releaseResources() 112 | { 113 | // When playback stops, you can use this as an opportunity to free up any 114 | // spare memory, etc. 115 | } 116 | 117 | void FilterGuiDemoAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) 118 | { 119 | float numSamples = buffer.getNumSamples(); 120 | float currentSampleRate = getSampleRate(); 121 | 122 | //Handles filter being added onto an already playing audio track where some hosts will not call prepare to play method. 123 | if (filter1->getSampleRate() != currentSampleRate) 124 | { 125 | filter1->initializeFilter(currentSampleRate, defaultMinFilterFrequency, defaultMaxFilterFrequency); 126 | } 127 | 128 | // In case we have more outputs than inputs, this code clears any output 129 | // channels that didn't contain input data, (because these aren't 130 | // guaranteed to be empty - they may contain garbage). 131 | // I've added this to avoid people getting screaming feedback 132 | // when they first compile the plugin, but obviously you don't need to 133 | // this code if your algorithm already fills all the output channels. 134 | for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i) 135 | buffer.clear (i, 0, buffer.getNumSamples()); 136 | 137 | // MAIN AUDIO PROCESSING BLOCK. PROCESS FILTER TWICE FOR STEREO CHANNELS 138 | for (int channel = 0; channel < getTotalNumInputChannels(); ++channel) 139 | { 140 | const float* input = buffer.getReadPointer(channel); 141 | float* output = buffer.getWritePointer (channel); 142 | 143 | for (int i = 0; i < numSamples; i++) 144 | { 145 | output[i] = filter1->processFilter(input[i], channel); 146 | } 147 | } 148 | } 149 | 150 | //============================================================================== 151 | AudioFilter& FilterGuiDemoAudioProcessor::getAudioFilter() 152 | { 153 | return *filter1; 154 | } 155 | 156 | //============================================================================== 157 | bool FilterGuiDemoAudioProcessor::hasEditor() const 158 | { 159 | return true; // (change this to false if you choose to not supply an editor) 160 | } 161 | 162 | AudioProcessorEditor* FilterGuiDemoAudioProcessor::createEditor() 163 | { 164 | return new FilterGuiDemoAudioProcessorEditor (*this); 165 | } 166 | 167 | //============================================================================== 168 | void FilterGuiDemoAudioProcessor::getStateInformation (MemoryBlock& destData) 169 | { 170 | // You should use this method to store your parameters in the memory block. 171 | // You could do that either as raw data, or use the XML or ValueTree classes 172 | // as intermediaries to make it easy to save and load complex data. 173 | 174 | // Create an outer XML element.. 175 | XmlElement xml ("MYPLUGINSETTINGS"); 176 | 177 | // Store the values of all our parameters, using their param ID as the XML attribute 178 | for (int i = 0; i < getNumParameters(); ++i) 179 | if (AudioProcessorParameterWithID* p = dynamic_cast (getParameters().getUnchecked(i))) 180 | xml.setAttribute (p->paramID, p->getValue()); 181 | 182 | // then use this helper function to stuff it into the binary blob and return it.. 183 | copyXmlToBinary (xml, destData); 184 | } 185 | 186 | void FilterGuiDemoAudioProcessor::setStateInformation (const void* data, int sizeInBytes) 187 | { 188 | // You should use this method to restore your parameters from this memory block, 189 | // whose contents will have been created by the getStateInformation() call. 190 | // This getXmlFromBinary() helper function retrieves our XML from the binary blob.. 191 | ScopedPointer xmlState (getXmlFromBinary (data, sizeInBytes)); 192 | 193 | if (xmlState != nullptr) 194 | { 195 | // make sure that it's actually our type of XML object.. 196 | if (xmlState->hasTagName ("MYPLUGINSETTINGS")) 197 | { 198 | // Now reload our parameters.. 199 | for (int i = 0; i < getNumParameters(); ++i) 200 | if (AudioProcessorParameterWithID* p = dynamic_cast (getParameters().getUnchecked(i))) 201 | p->setValueNotifyingHost ((float) xmlState->getDoubleAttribute (p->paramID, p->getValue())); 202 | } 203 | } 204 | } 205 | 206 | //============================================================================== 207 | // This creates new instances of the plugin.. 208 | AudioProcessor* JUCE_CALLTYPE createPluginFilter() 209 | { 210 | return new FilterGuiDemoAudioProcessor(); 211 | } 212 | -------------------------------------------------------------------------------- /FilterGuiDemo.jucer: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | 19 | 21 | 22 | 23 | 25 | 27 | 29 | 31 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 42 | 43 | 45 | 47 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /JuceLibraryCode/AppConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | There's a section below where you can add your own custom code safely, and the 7 | Introjucer will preserve the contents of that block, but the best way to change 8 | any of these definitions is by using the Introjucer's project settings. 9 | 10 | Any commented-out settings will assume their default values. 11 | 12 | */ 13 | 14 | #ifndef __JUCE_APPCONFIG_AOG7SJ__ 15 | #define __JUCE_APPCONFIG_AOG7SJ__ 16 | 17 | //============================================================================== 18 | // [BEGIN_USER_CODE_SECTION] 19 | 20 | // (You can add your own code in this section, and the Introjucer will not overwrite it) 21 | 22 | // [END_USER_CODE_SECTION] 23 | 24 | //============================================================================== 25 | #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 26 | #define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 27 | #define JUCE_MODULE_AVAILABLE_juce_audio_formats 1 28 | #define JUCE_MODULE_AVAILABLE_juce_audio_plugin_client 1 29 | #define JUCE_MODULE_AVAILABLE_juce_audio_processors 1 30 | #define JUCE_MODULE_AVAILABLE_juce_core 1 31 | #define JUCE_MODULE_AVAILABLE_juce_cryptography 1 32 | #define JUCE_MODULE_AVAILABLE_juce_data_structures 1 33 | #define JUCE_MODULE_AVAILABLE_juce_events 1 34 | #define JUCE_MODULE_AVAILABLE_juce_graphics 1 35 | #define JUCE_MODULE_AVAILABLE_juce_gui_basics 1 36 | #define JUCE_MODULE_AVAILABLE_juce_gui_extra 1 37 | #define JUCE_MODULE_AVAILABLE_juce_opengl 1 38 | #define JUCE_MODULE_AVAILABLE_juce_video 1 39 | 40 | //============================================================================== 41 | #ifndef JUCE_STANDALONE_APPLICATION 42 | #define JUCE_STANDALONE_APPLICATION 0 43 | #endif 44 | 45 | //============================================================================== 46 | // juce_audio_devices flags: 47 | 48 | #ifndef JUCE_ASIO 49 | //#define JUCE_ASIO 50 | #endif 51 | 52 | #ifndef JUCE_WASAPI 53 | //#define JUCE_WASAPI 54 | #endif 55 | 56 | #ifndef JUCE_WASAPI_EXCLUSIVE 57 | //#define JUCE_WASAPI_EXCLUSIVE 58 | #endif 59 | 60 | #ifndef JUCE_DIRECTSOUND 61 | //#define JUCE_DIRECTSOUND 62 | #endif 63 | 64 | #ifndef JUCE_ALSA 65 | //#define JUCE_ALSA 66 | #endif 67 | 68 | #ifndef JUCE_JACK 69 | //#define JUCE_JACK 70 | #endif 71 | 72 | #ifndef JUCE_USE_ANDROID_OPENSLES 73 | //#define JUCE_USE_ANDROID_OPENSLES 74 | #endif 75 | 76 | #ifndef JUCE_USE_CDREADER 77 | //#define JUCE_USE_CDREADER 78 | #endif 79 | 80 | #ifndef JUCE_USE_CDBURNER 81 | //#define JUCE_USE_CDBURNER 82 | #endif 83 | 84 | //============================================================================== 85 | // juce_audio_formats flags: 86 | 87 | #ifndef JUCE_USE_FLAC 88 | //#define JUCE_USE_FLAC 89 | #endif 90 | 91 | #ifndef JUCE_USE_OGGVORBIS 92 | //#define JUCE_USE_OGGVORBIS 93 | #endif 94 | 95 | #ifndef JUCE_USE_MP3AUDIOFORMAT 96 | //#define JUCE_USE_MP3AUDIOFORMAT 97 | #endif 98 | 99 | #ifndef JUCE_USE_LAME_AUDIO_FORMAT 100 | //#define JUCE_USE_LAME_AUDIO_FORMAT 101 | #endif 102 | 103 | #ifndef JUCE_USE_WINDOWS_MEDIA_FORMAT 104 | //#define JUCE_USE_WINDOWS_MEDIA_FORMAT 105 | #endif 106 | 107 | //============================================================================== 108 | // juce_audio_processors flags: 109 | 110 | #ifndef JUCE_PLUGINHOST_VST 111 | //#define JUCE_PLUGINHOST_VST 112 | #endif 113 | 114 | #ifndef JUCE_PLUGINHOST_VST3 115 | //#define JUCE_PLUGINHOST_VST3 116 | #endif 117 | 118 | #ifndef JUCE_PLUGINHOST_AU 119 | //#define JUCE_PLUGINHOST_AU 120 | #endif 121 | 122 | //============================================================================== 123 | // juce_core flags: 124 | 125 | #ifndef JUCE_FORCE_DEBUG 126 | //#define JUCE_FORCE_DEBUG 127 | #endif 128 | 129 | #ifndef JUCE_LOG_ASSERTIONS 130 | //#define JUCE_LOG_ASSERTIONS 131 | #endif 132 | 133 | #ifndef JUCE_CHECK_MEMORY_LEAKS 134 | //#define JUCE_CHECK_MEMORY_LEAKS 135 | #endif 136 | 137 | #ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 138 | //#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 139 | #endif 140 | 141 | #ifndef JUCE_INCLUDE_ZLIB_CODE 142 | //#define JUCE_INCLUDE_ZLIB_CODE 143 | #endif 144 | 145 | #ifndef JUCE_USE_CURL 146 | //#define JUCE_USE_CURL 147 | #endif 148 | 149 | //============================================================================== 150 | // juce_graphics flags: 151 | 152 | #ifndef JUCE_USE_COREIMAGE_LOADER 153 | //#define JUCE_USE_COREIMAGE_LOADER 154 | #endif 155 | 156 | #ifndef JUCE_USE_DIRECTWRITE 157 | //#define JUCE_USE_DIRECTWRITE 158 | #endif 159 | 160 | //============================================================================== 161 | // juce_gui_basics flags: 162 | 163 | #ifndef JUCE_ENABLE_REPAINT_DEBUGGING 164 | //#define JUCE_ENABLE_REPAINT_DEBUGGING 165 | #endif 166 | 167 | #ifndef JUCE_USE_XSHM 168 | //#define JUCE_USE_XSHM 169 | #endif 170 | 171 | #ifndef JUCE_USE_XRENDER 172 | //#define JUCE_USE_XRENDER 173 | #endif 174 | 175 | #ifndef JUCE_USE_XCURSOR 176 | //#define JUCE_USE_XCURSOR 177 | #endif 178 | 179 | //============================================================================== 180 | // juce_gui_extra flags: 181 | 182 | #ifndef JUCE_WEB_BROWSER 183 | //#define JUCE_WEB_BROWSER 184 | #endif 185 | 186 | #ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR 187 | //#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 188 | #endif 189 | 190 | //============================================================================== 191 | // juce_video flags: 192 | 193 | #ifndef JUCE_DIRECTSHOW 194 | //#define JUCE_DIRECTSHOW 195 | #endif 196 | 197 | #ifndef JUCE_MEDIAFOUNDATION 198 | //#define JUCE_MEDIAFOUNDATION 199 | #endif 200 | 201 | #ifndef JUCE_QUICKTIME 202 | #define JUCE_QUICKTIME 0 203 | #endif 204 | 205 | #ifndef JUCE_USE_CAMERA 206 | //#define JUCE_USE_CAMERA 207 | #endif 208 | 209 | 210 | //============================================================================== 211 | // Audio plugin settings.. 212 | 213 | #ifndef JucePlugin_Build_VST 214 | #define JucePlugin_Build_VST 0 215 | #endif 216 | #ifndef JucePlugin_Build_VST3 217 | #define JucePlugin_Build_VST3 0 218 | #endif 219 | #ifndef JucePlugin_Build_AU 220 | #define JucePlugin_Build_AU 1 221 | #endif 222 | #ifndef JucePlugin_Build_RTAS 223 | #define JucePlugin_Build_RTAS 0 224 | #endif 225 | #ifndef JucePlugin_Build_AAX 226 | #define JucePlugin_Build_AAX 0 227 | #endif 228 | #ifndef JucePlugin_Name 229 | #define JucePlugin_Name "FilterGuiDemo" 230 | #endif 231 | #ifndef JucePlugin_Desc 232 | #define JucePlugin_Desc "FilterGuiDemo" 233 | #endif 234 | #ifndef JucePlugin_Manufacturer 235 | #define JucePlugin_Manufacturer "RudeAudio" 236 | #endif 237 | #ifndef JucePlugin_ManufacturerWebsite 238 | #define JucePlugin_ManufacturerWebsite "" 239 | #endif 240 | #ifndef JucePlugin_ManufacturerEmail 241 | #define JucePlugin_ManufacturerEmail "josh.marler@hotmail.co.uk" 242 | #endif 243 | #ifndef JucePlugin_ManufacturerCode 244 | #define JucePlugin_ManufacturerCode 'RUDE' 245 | #endif 246 | #ifndef JucePlugin_PluginCode 247 | #define JucePlugin_PluginCode 'RFil' 248 | #endif 249 | #ifndef JucePlugin_IsSynth 250 | #define JucePlugin_IsSynth 0 251 | #endif 252 | #ifndef JucePlugin_WantsMidiInput 253 | #define JucePlugin_WantsMidiInput 0 254 | #endif 255 | #ifndef JucePlugin_ProducesMidiOutput 256 | #define JucePlugin_ProducesMidiOutput 0 257 | #endif 258 | #ifndef JucePlugin_IsMidiEffect 259 | #define JucePlugin_IsMidiEffect 0 260 | #endif 261 | #ifndef JucePlugin_SilenceInProducesSilenceOut 262 | #define JucePlugin_SilenceInProducesSilenceOut 0 263 | #endif 264 | #ifndef JucePlugin_EditorRequiresKeyboardFocus 265 | #define JucePlugin_EditorRequiresKeyboardFocus 0 266 | #endif 267 | #ifndef JucePlugin_Version 268 | #define JucePlugin_Version 1.0.0 269 | #endif 270 | #ifndef JucePlugin_VersionCode 271 | #define JucePlugin_VersionCode 0x10000 272 | #endif 273 | #ifndef JucePlugin_VersionString 274 | #define JucePlugin_VersionString "1.0.0" 275 | #endif 276 | #ifndef JucePlugin_VSTUniqueID 277 | #define JucePlugin_VSTUniqueID JucePlugin_PluginCode 278 | #endif 279 | #ifndef JucePlugin_VSTCategory 280 | #define JucePlugin_VSTCategory kPlugCategEffect 281 | #endif 282 | #ifndef JucePlugin_AUMainType 283 | #define JucePlugin_AUMainType kAudioUnitType_Effect 284 | #endif 285 | #ifndef JucePlugin_AUSubType 286 | #define JucePlugin_AUSubType JucePlugin_PluginCode 287 | #endif 288 | #ifndef JucePlugin_AUExportPrefix 289 | #define JucePlugin_AUExportPrefix FilterGuiDemoAU 290 | #endif 291 | #ifndef JucePlugin_AUExportPrefixQuoted 292 | #define JucePlugin_AUExportPrefixQuoted "FilterGuiDemoAU" 293 | #endif 294 | #ifndef JucePlugin_AUManufacturerCode 295 | #define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode 296 | #endif 297 | #ifndef JucePlugin_CFBundleIdentifier 298 | #define JucePlugin_CFBundleIdentifier com.RudeAudio.FilterGuiDemo 299 | #endif 300 | #ifndef JucePlugin_RTASCategory 301 | #define JucePlugin_RTASCategory ePlugInCategory_None 302 | #endif 303 | #ifndef JucePlugin_RTASManufacturerCode 304 | #define JucePlugin_RTASManufacturerCode JucePlugin_ManufacturerCode 305 | #endif 306 | #ifndef JucePlugin_RTASProductId 307 | #define JucePlugin_RTASProductId JucePlugin_PluginCode 308 | #endif 309 | #ifndef JucePlugin_RTASDisableBypass 310 | #define JucePlugin_RTASDisableBypass 0 311 | #endif 312 | #ifndef JucePlugin_RTASDisableMultiMono 313 | #define JucePlugin_RTASDisableMultiMono 0 314 | #endif 315 | #ifndef JucePlugin_AAXIdentifier 316 | #define JucePlugin_AAXIdentifier com.yourcompany.FilterGuiDemo 317 | #endif 318 | #ifndef JucePlugin_AAXManufacturerCode 319 | #define JucePlugin_AAXManufacturerCode JucePlugin_ManufacturerCode 320 | #endif 321 | #ifndef JucePlugin_AAXProductId 322 | #define JucePlugin_AAXProductId JucePlugin_PluginCode 323 | #endif 324 | #ifndef JucePlugin_AAXCategory 325 | #define JucePlugin_AAXCategory AAX_ePlugInCategory_Dynamics 326 | #endif 327 | #ifndef JucePlugin_AAXDisableBypass 328 | #define JucePlugin_AAXDisableBypass 0 329 | #endif 330 | #ifndef JucePlugin_AAXDisableMultiMono 331 | #define JucePlugin_AAXDisableMultiMono 0 332 | #endif 333 | 334 | #endif // __JUCE_APPCONFIG_AOG7SJ__ 335 | -------------------------------------------------------------------------------- /Source/GUIComponents/CustomLookAndFeel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | CustomLookAndFeel.cpp 5 | Created: 19 Sep 2014 7:23:32pm 6 | Author: Joshua Marler 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "CustomLookAndFeel.h" 12 | 13 | 14 | //Basic override of drawRotarySlider function 15 | 16 | /* 17 | Uses path data from SVG to path data conversion tool in Introjucer/Projucer - works best if the SVG file used is composed of a 18 | single vector / path. Change the pathData[] variable to whatever path data the SVG conversion tool spits out at you. 19 | 20 | Pay attention to this line in the drawRotarySlider function - svgPath.scaleToFit(-innerRadius, -innerRadius, innerRadius * 2.0f, innerRadius * 2.0f, true); 21 | 22 | This line scales the converted SVG path to fit inside the rotary sliders outer arc/circle. This is necessary only for this particular slider style and could be changed. 23 | */ 24 | 25 | void CustomLookAndFeel::drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos, 26 | const float rotaryStartAngle, const float rotaryEndAngle, Slider& slider) 27 | { 28 | const float radius = jmin (width / 2, height / 2) - 2.0f; 29 | const float centreX = x + width * 0.5f; 30 | const float centreY = y + height * 0.5f; 31 | const float rx = centreX - radius; 32 | const float ry = centreY - radius; 33 | const float rw = radius * 2.0f; 34 | const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); 35 | const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); 36 | 37 | if (radius > 12.0f) 38 | { 39 | if (slider.isEnabled()) 40 | g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f)); 41 | else 42 | g.setColour (Colour (0x80808080)); 43 | 44 | const float thickness = 0.75f; 45 | 46 | { 47 | Path filledArc; 48 | filledArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, angle, thickness); 49 | 50 | g.fillPath (filledArc); 51 | } 52 | 53 | if (thickness > 0) 54 | { 55 | const float innerRadius = radius * 0.5f; 56 | Path p; 57 | 58 | 59 | static const unsigned char pathData[] = { 110,109,102,38,152,67,154,25,94,67,98,51,115,152,67,154,25,94,67,0,192,152,67,52,51,94,67,204,12,153,67,205,76,94,67,98,102,230,152,67,205,76,94,67,204,204,152,67,205,76,94,67,204,204,152,67,205,76,94,67,98,102,166,152,67,205,76,94,67,204,12,154,67,51, 60 | 179,94,67,102,38,154,67,51,179,94,67,98,153,217,154,67,102,230,94,67,0,128,155,67,102,102,95,67,51,51,156,67,51,179,95,67,98,205,140,157,67,102,102,96,67,205,204,158,67,102,102,97,67,153,25,160,67,102,102,98,67,98,51,51,160,67,0,128,98,67,204,12,161, 61 | 67,204,76,99,67,204,76,161,67,0,128,99,67,98,50,243,161,67,51,51,100,67,204,140,162,67,102,230,100,67,50,51,163,67,154,153,101,67,98,152,89,164,67,103,230,102,67,50,115,165,67,0,128,104,67,204,140,166,67,52,51,106,67,98,255,191,166,67,103,102,106,67, 62 | 153,217,166,67,154,153,106,67,50,243,166,67,206,204,106,67,98,255,255,166,67,104,230,106,67,204,12,167,67,1,0,107,67,152,25,167,67,155,25,107,67,98,152,153,167,67,1,0,108,67,152,25,168,67,1,0,109,67,203,140,168,67,104,230,109,67,98,254,127,169,67,206, 63 | 204,111,67,101,102,170,67,206,204,113,67,254,63,171,67,2,0,116,67,98,152,89,171,67,53,51,116,67,254,255,171,67,156,25,118,67,203,12,172,67,53,51,118,67,98,101,102,172,67,207,76,119,67,254,191,172,67,2,128,120,67,152,25,173,67,53,179,121,67,98,203,204, 64 | 173,67,155,25,124,67,101,102,174,67,2,128,126,67,50,243,174,67,206,140,128,67,98,255,255,174,67,104,166,128,67,204,12,175,67,1,192,128,67,152,25,175,67,155,217,128,67,98,101,38,175,67,53,243,128,67,101,38,175,67,206,12,129,67,50,51,175,67,53,51,129,67, 65 | 98,50,115,175,67,155,217,129,67,101,166,175,67,207,140,130,67,204,204,175,67,53,51,131,67,98,50,51,176,67,155,153,132,67,50,115,176,67,104,230,133,67,50,179,176,67,155,89,135,67,98,255,191,176,67,104,166,135,67,255,191,176,67,206,204,135,67,255,191,176, 66 | 67,1,0,136,67,98,153,217,176,67,1,0,137,67,153,217,176,67,52,243,137,67,153,217,176,67,103,230,138,67,98,153,217,176,67,154,153,139,67,204,204,176,67,154,89,140,67,204,204,176,67,205,12,141,67,98,204,204,176,67,0,64,141,67,102,166,176,67,0,128,142,67, 67 | 50,179,176,67,0,128,142,67,98,255,127,176,67,102,166,143,67,255,63,176,67,205,204,144,67,255,255,175,67,102,230,145,67,98,153,217,175,67,153,153,146,67,153,153,175,67,204,76,147,67,101,102,175,67,102,230,147,67,98,152,89,175,67,204,12,148,67,50,243,174, 68 | 67,153,25,149,67,50,243,174,67,0,64,149,67,98,101,102,174,67,205,140,150,67,204,204,173,67,51,179,151,67,152,25,173,67,102,230,152,67,98,254,191,172,67,0,128,153,67,101,102,172,67,153,25,154,67,203,12,172,67,102,166,154,67,98,254,255,171,67,51,179,154, 69 | 67,49,243,171,67,204,204,154,67,101,230,171,67,153,217,154,67,98,152,217,171,67,51,243,154,67,255,191,171,67,204,12,155,67,101,166,171,67,51,51,155,67,98,152,217,170,67,205,76,156,67,50,243,169,67,205,76,157,67,255,255,168,67,205,76,158,67,98,204,140, 70 | 168,67,205,204,158,67,204,12,168,67,205,76,159,67,204,140,167,67,51,179,159,67,98,50,115,167,67,205,204,159,67,102,166,166,67,0,128,160,67,204,140,166,67,153,153,160,67,98,50,115,165,67,51,115,161,67,204,76,164,67,102,38,162,67,102,38,163,67,102,230, 71 | 162,67,98,204,140,162,67,0,64,163,67,51,243,161,67,204,140,163,67,102,102,161,67,102,230,163,67,98,51,51,161,67,0,0,164,67,204,12,161,67,204,12,164,67,51,243,160,67,153,25,164,67,98,102,230,160,67,102,38,164,67,205,204,160,67,102,38,164,67,51,179,160, 72 | 67,51,51,164,67,98,51,115,159,67,0,192,164,67,102,38,158,67,51,51,165,67,153,217,156,67,153,153,165,67,98,204,140,155,67,255,255,165,67,255,63,154,67,255,63,166,67,51,243,152,67,255,127,166,67,98,153,89,153,67,50,115,166,67,51,115,153,67,101,102,166, 73 | 67,102,102,153,67,101,102,166,67,98,0,64,153,67,101,102,166,67,153,25,152,67,203,140,166,67,0,0,152,67,203,140,166,67,98,205,12,151,67,101,166,166,67,154,25,150,67,101,166,166,67,102,38,149,67,101,166,166,67,98,204,12,149,67,101,166,166,67,0,0,149,67, 74 | 101,166,166,67,102,230,148,67,101,166,166,67,98,102,166,147,67,101,166,166,67,51,115,146,67,203,140,166,67,51,51,145,67,50,115,166,67,98,153,89,145,67,50,115,166,67,51,115,145,67,50,115,166,67,51,115,145,67,50,115,166,67,98,153,153,145,67,50,115,166, 75 | 67,51,51,144,67,255,63,166,67,153,25,144,67,255,63,166,67,98,102,102,143,67,101,38,166,67,255,191,142,67,101,230,165,67,204,12,142,67,255,191,165,67,98,255,191,140,67,101,102,165,67,50,115,139,67,101,230,164,67,102,38,138,67,101,102,164,67,98,204,12, 76 | 138,67,152,89,164,67,153,25,137,67,101,230,163,67,51,243,136,67,152,217,163,67,98,205,76,136,67,254,127,163,67,51,179,135,67,50,51,163,67,153,25,135,67,203,204,162,67,98,51,243,133,67,101,38,162,67,153,217,132,67,152,89,161,67,255,191,131,67,254,127, 77 | 160,67,98,153,153,131,67,100,102,160,67,50,115,131,67,203,76,160,67,153,89,131,67,49,51,160,67,98,204,76,131,67,100,38,160,67,255,63,131,67,151,25,160,67,102,38,131,67,203,12,160,67,98,102,166,130,67,152,153,159,67,102,38,130,67,152,25,159,67,51,179, 78 | 129,67,101,166,158,67,98,0,192,128,67,50,179,157,67,51,179,127,67,50,179,156,67,0,0,126,67,152,153,155,67,98,205,204,125,67,254,127,155,67,154,153,124,67,152,153,154,67,102,102,124,67,254,127,154,67,98,153,153,123,67,100,230,153,67,102,230,122,67,152, 79 | 89,153,67,51,51,122,67,254,191,152,67,98,205,204,120,67,203,140,151,67,153,153,119,67,152,89,150,67,0,128,118,67,203,12,149,67,98,102,102,118,67,49,243,148,67,205,76,118,67,152,217,148,67,51,51,118,67,254,191,148,67,98,153,25,118,67,100,166,148,67,153, 80 | 25,118,67,203,140,148,67,0,0,118,67,100,102,148,67,98,0,128,117,67,254,191,147,67,154,25,117,67,202,12,147,67,205,204,116,67,100,102,146,67,98,0,0,116,67,254,255,144,67,0,128,115,67,49,179,143,67,0,0,115,67,254,63,142,67,98,102,230,114,67,49,243,141, 81 | 67,102,230,114,67,203,204,141,67,102,230,114,67,152,153,141,67,98,51,179,114,67,152,153,140,67,51,179,114,67,101,166,139,67,51,179,114,67,50,179,138,67,98,51,179,114,67,255,255,137,67,205,204,114,67,255,63,137,67,205,204,114,67,204,140,136,67,98,205, 82 | 204,114,67,153,89,136,67,154,25,115,67,153,25,135,67,0,0,115,67,153,25,135,67,98,102,102,115,67,51,243,133,67,102,230,115,67,204,204,132,67,102,102,116,67,51,179,131,67,98,51,179,116,67,0,0,131,67,51,51,117,67,205,76,130,67,153,153,117,67,51,179,129, 83 | 67,98,51,179,117,67,205,140,129,67,255,127,118,67,0,128,128,67,255,127,118,67,153,89,128,67,98,153,153,119,67,152,25,126,67,204,204,120,67,204,204,123,67,50,51,122,67,101,102,121,67,98,101,230,122,67,50,51,120,67,152,153,123,67,152,25,119,67,204,76,124, 84 | 67,101,230,117,67,98,102,102,124,67,50,179,117,67,255,127,124,67,152,153,117,67,153,153,124,67,203,76,117,67,98,51,179,124,67,49,51,117,67,102,230,124,67,254,255,116,67,153,25,125,67,152,153,116,67,98,51,179,126,67,101,102,114,67,0,64,128,67,101,102, 85 | 112,67,51,51,129,67,254,127,110,67,98,102,166,129,67,254,127,109,67,102,38,130,67,254,127,108,67,102,166,130,67,49,179,107,67,98,0,192,130,67,254,127,107,67,204,140,131,67,151,25,106,67,102,166,131,67,100,230,105,67,98,0,192,132,67,49,51,104,67,102,230, 86 | 133,67,202,204,102,67,204,12,135,67,202,76,101,67,98,102,166,135,67,151,153,100,67,255,63,136,67,253,255,99,67,204,204,136,67,202,76,99,67,98,50,243,136,67,151,25,99,67,153,25,137,67,253,255,98,67,50,51,137,67,100,230,98,67,98,204,76,137,67,202,204,98, 87 | 67,152,89,137,67,202,204,98,67,50,115,137,67,49,179,98,67,98,50,179,138,67,151,153,97,67,255,255,139,67,49,179,96,67,204,76,141,67,100,230,95,67,98,153,153,142,67,151,25,95,67,102,230,143,67,151,153,94,67,50,51,145,67,151,25,94,67,98,152,217,144,67,49, 88 | 51,94,67,255,191,144,67,202,76,94,67,255,191,144,67,202,76,94,67,98,153,217,144,67,202,76,94,67,101,166,145,67,151,25,94,67,255,255,145,67,253,255,93,67,108,255,255,145,67,99,102,123,67,108,204,12,152,67,99,102,123,67,108,204,12,152,67,48,51,94,67,108, 89 | 102,38,152,67,154,25,94,67,108,102,38,152,67,154,25,94,67,99,101,0,0 }; 90 | 91 | 92 | Path svgPath; 93 | svgPath.loadPathFromData (pathData, sizeof (pathData)); 94 | 95 | // Fit svgpath to inner dimensions of outlineArc 96 | svgPath.scaleToFit(-innerRadius, -innerRadius, innerRadius * 2.0f, innerRadius * 2.0f, true); 97 | 98 | p.addEllipse(-innerRadius, -innerRadius, innerRadius * 2.0f, innerRadius * 2.0f); 99 | g.fillPath (svgPath, AffineTransform::rotation (angle).translated (centreX, centreY)); 100 | } 101 | 102 | if (slider.isEnabled()) 103 | g.setColour (slider.findColour (Slider::rotarySliderOutlineColourId)); 104 | else 105 | g.setColour (Colour (0x80808080)); 106 | 107 | 108 | Path outlineArc; 109 | outlineArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, thickness); 110 | 111 | outlineArc.closeSubPath(); 112 | 113 | g.strokePath (outlineArc, PathStrokeType (slider.isEnabled() ? (isMouseOver ? 2.0f : 1.2f) : 0.3f)); 114 | } 115 | 116 | else 117 | { 118 | if (slider.isEnabled()) 119 | g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha 120 | (isMouseOver ? 1.0f : 0.7f)); 121 | else 122 | g.setColour (Colour (0x80808080)); 123 | 124 | Path p; 125 | 126 | g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY)); 127 | } 128 | } 129 | 130 | --------------------------------------------------------------------------------