├── plugin ├── Resources │ ├── resonarium_logo.png │ ├── Fonts │ │ ├── Jost-200-Thin.otf │ │ ├── Jost-300-Light.otf │ │ ├── Jost-400-Book.otf │ │ ├── Jost-500-Medium.otf │ │ ├── Jost-600-Semi.otf │ │ ├── Jost-700-Bold.otf │ │ ├── Jost-800-Hevy.otf │ │ ├── Jost-900-Black.otf │ │ ├── Jost-100-Hairline.otf │ │ ├── Jost-200-ThinItalic.otf │ │ ├── Jost-300-LightItalic.otf │ │ ├── Jost-400-BookItalic.otf │ │ ├── Jost-600-SemiItalic.otf │ │ ├── Jost-700-BoldItalic.otf │ │ ├── Jost-800-HevyItalic.otf │ │ ├── Jost-900-BlackItalic.otf │ │ ├── Jost-500-MediumItalic.otf │ │ └── Jost-100-HairlineItalic.otf │ ├── music_note_2.svg │ ├── music_note.svg │ ├── letter_s.svg │ ├── resonarium_logo_svg.svg │ ├── Presets │ │ └── Wooden Bells.xml │ └── Presets_old │ │ ├── Harmonious Ascendence.xml │ │ ├── Simple Bass.xml │ │ ├── Raindrops from the Other Side.xml │ │ └── Phastic Square Pluck.xml └── Source │ ├── util │ ├── WrappedEnvelope.cpp │ ├── ResonariumUtilities.h │ ├── StereoMSEGWrapper.cpp │ ├── StereoLFOWrapper.cpp │ ├── WrappedEnvelope.h │ ├── StereoMSEGWrapper.h │ ├── StereoLFOWrapper.h │ ├── ResonariumUtilities.cpp │ ├── RandomLFO.h │ ├── InterpolatedValue.h │ ├── InterpolatedParameter.h │ └── RandomLFO.cpp │ ├── dsp │ ├── WrappedSVF.cpp │ ├── Sampler.h │ ├── Filters.h │ ├── Filters.cpp │ ├── ResonariumEffectChain.h │ ├── Distortion.h │ ├── WrappedSVF.h │ ├── MultiFilter.h │ ├── MultiAmp.cpp │ ├── airwindows │ │ ├── DeRez2.h │ │ ├── BigAmp.h │ │ ├── FXBase.h │ │ ├── FireAmp.h │ │ ├── LeadAmp.h │ │ ├── GrindAmp.h │ │ ├── BassAmp.h │ │ ├── DeRez2.cpp │ │ └── BigAmp.cpp │ ├── MultiDelay.cpp │ ├── Sampler.cpp │ ├── MultiAmp.h │ ├── Distortion.cpp │ ├── MultiFilter.cpp │ ├── MultiDelay.h │ └── ResonariumEffectChain.cpp │ ├── ui │ ├── AnimatedScrollBarsViewport.cpp │ ├── RandomLFOComponent.h │ ├── SettingsPanel.h │ ├── SampleDropperComponent.h │ ├── ResonariumLookAndFeel.h │ ├── RandomLFOComponent.cpp │ ├── ClickThruSelect.h │ ├── SettingsPanel.cpp │ └── AnimatedScrollBarsViewport.h │ ├── ResonatorBank.h │ ├── ResonatorSynth.h │ ├── defines.h │ ├── GlobalState.h │ ├── WaveguideResonatorBank.h │ ├── PluginProcessor.h │ ├── ResonatorVoice.h │ ├── PluginEditor.h │ ├── StereoResonator.h │ └── ResonatorSynth.cpp ├── CMakePresets.json ├── .gitignore ├── .gitmodules ├── python-examples └── simple-demo.py └── python-bindings.md /plugin/Resources/resonarium_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/resonarium_logo.png -------------------------------------------------------------------------------- /plugin/Source/util/WrappedEnvelope.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Gabriel Soule on 7/17/24. 3 | // 4 | 5 | #include "WrappedEnvelope.h" 6 | -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-200-Thin.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-200-Thin.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-300-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-300-Light.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-400-Book.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-400-Book.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-500-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-500-Medium.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-600-Semi.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-600-Semi.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-700-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-700-Bold.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-800-Hevy.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-800-Hevy.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-900-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-900-Black.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-100-Hairline.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-100-Hairline.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-200-ThinItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-200-ThinItalic.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-300-LightItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-300-LightItalic.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-400-BookItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-400-BookItalic.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-600-SemiItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-600-SemiItalic.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-700-BoldItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-700-BoldItalic.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-800-HevyItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-800-HevyItalic.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-900-BlackItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-900-BlackItalic.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-500-MediumItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-500-MediumItalic.otf -------------------------------------------------------------------------------- /plugin/Resources/Fonts/Jost-100-HairlineItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielsoule/resonarium/HEAD/plugin/Resources/Fonts/Jost-100-HairlineItalic.otf -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmakeMinimumRequired": { 3 | "major": 3, 4 | "minor": 24, 5 | "patch": 0 6 | }, 7 | "version": 5, 8 | "include": [ 9 | "modules/gin/ci/toolchains/xcode.json", 10 | "modules/gin/ci/toolchains/vs.json", 11 | "modules/gin/ci/toolchains/gcc.json" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /plugin/Source/dsp/WrappedSVF.cpp: -------------------------------------------------------------------------------- 1 | #include "WrappedSVF.h" 2 | 3 | void WrappedSVF::prepare(const juce::dsp::ProcessSpec& spec) 4 | { 5 | svfL.prepare({ spec.sampleRate, spec.maximumBlockSize, 1 }); 6 | svfR.prepare({ spec.sampleRate, spec.maximumBlockSize, 1 }); 7 | } 8 | 9 | void WrappedSVF::reset() 10 | { 11 | svfL.reset(); 12 | svfR.reset(); 13 | } 14 | -------------------------------------------------------------------------------- /plugin/Source/ui/AnimatedScrollBarsViewport.cpp: -------------------------------------------------------------------------------- 1 | #include "AnimatedScrollBarsViewport.h" 2 | 3 | bool AnimatedScrollBarsViewport::isKnobComponent(juce::Component* component) 4 | { 5 | if (!component) 6 | return false; 7 | 8 | // Simple string-based detection - check if the component name contains "Knob" 9 | auto className = juce::String(typeid(*component).name()); 10 | return className.contains("Knob"); 11 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | .DS_Store 35 | 36 | Builds 37 | cmake-build-debug 38 | .idea/ 39 | 40 | # Claude.md file for Claude Code AI helpers 41 | CLAUDE.md -------------------------------------------------------------------------------- /plugin/Source/ResonatorBank.h: -------------------------------------------------------------------------------- 1 | #ifndef RESONATORBANK_H 2 | #define RESONATORBANK_H 3 | 4 | #include 5 | 6 | class ResonatorVoice; 7 | 8 | class ResonatorBank { 9 | public: 10 | ResonatorBank(ResonatorVoice& parentVoice) : voice(parentVoice) {} 11 | virtual ~ResonatorBank() = default; 12 | 13 | virtual void process(juce::dsp::AudioBlock& exciterBlock, juce::dsp::AudioBlock& outputBlock) = 0; 14 | virtual void reset() = 0; 15 | virtual void prepare(const juce::dsp::ProcessSpec& spec) = 0; 16 | virtual void updateParameters(float newFrequency, int numSamples) = 0; 17 | 18 | ResonatorVoice& voice; 19 | int resonatorBankIndex = -1; 20 | }; 21 | 22 | 23 | 24 | #endif //RESONATORBANK_H 25 | -------------------------------------------------------------------------------- /plugin/Resources/music_note_2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugin/Source/util/ResonariumUtilities.h: -------------------------------------------------------------------------------- 1 | #ifndef RESONARIUMUTILITIES_H 2 | #define RESONARIUMUTILITIES_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class ResonariumUtilities 9 | { 10 | public: 11 | /** 12 | * @return a 14-bit MPE pitch bend number that bends the current MIDI note number to the target frequency (as close as possible). 13 | */ 14 | static int calculateMPEPitchBendForFrequency(float targetFreq, 15 | int currentNoteNumber, 16 | float pitchBendRange); 17 | 18 | /** 19 | * Take a snapshot of the given component and save it to the specified file. 20 | */ 21 | static bool saveComponentToImage(juce::Component& comp, 22 | const juce::File& file, 23 | float scaleFactor = 1.0f); 24 | 25 | }; 26 | 27 | #endif //RESONARIUMUTILITIES_H -------------------------------------------------------------------------------- /plugin/Source/dsp/Sampler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Sampler 7 | { 8 | public: 9 | Sampler(); 10 | ~Sampler(); 11 | 12 | void prepare(const juce::dsp::ProcessSpec& spec); 13 | bool loadFile(const juce::File& file); 14 | void clear(); 15 | float getSample(int channel, int position) const; 16 | juce::dsp::AudioBlock getSubBlock(int start, int length); 17 | juce::AudioFormatManager& getFormatManager(); 18 | int getNumSamples() const; 19 | int getNumChannels() const; 20 | double getSampleRate() const; 21 | bool isLoaded() const; 22 | double getFileSampleRate() const; 23 | juce::String& getFilePath(); 24 | juce::String& getSampleName(); 25 | 26 | private: 27 | juce::AudioBuffer sampleBuffer; 28 | juce::AudioFormatManager formatManager; 29 | double sampleRate; 30 | double fileSampleRate; 31 | juce::String sampleName = ""; 32 | juce::String path = ""; 33 | bool loaded = false; 34 | }; 35 | -------------------------------------------------------------------------------- /plugin/Resources/music_note.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /plugin/Source/dsp/Filters.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERS_H 2 | #define FILTERS_H 3 | 4 | #include 5 | 6 | /** 7 | * A first order allpass filter based on a lattice structure. 8 | * 9 | */ 10 | class DispersionFilter 11 | { 12 | public: 13 | float processSample(float input); 14 | void prepare(juce::dsp::ProcessSpec spec); 15 | void reset(); 16 | void setDispersionAmount(float amount); 17 | 18 | float state[2] = {0, 0}; 19 | float c = 1.0f; 20 | float s = 0.0f; 21 | }; 22 | 23 | /** 24 | * A one-zero zero-pole filter, i.e. a weighted two-sample average, as used in the original Karplus-Strong algorithm. 25 | * What could be simpler? Not much! 26 | * Perfection is achieved not when there is nothing more to add, 27 | * but when there is nothing left to take away. Or something like that. 28 | */ 29 | class OneZeroFilter 30 | { 31 | public: 32 | float processSample(float input); 33 | void prepare(juce::dsp::ProcessSpec spec); 34 | void reset(); 35 | void setBrightness(float brightness); 36 | 37 | float state = 0; 38 | float p = 0.5; 39 | }; 40 | 41 | #endif //FILTERS_H 42 | -------------------------------------------------------------------------------- /plugin/Resources/letter_s.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /plugin/Source/ui/RandomLFOComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Parameters.h" 4 | 5 | class RandomLFOComponent : public juce::Component, private juce::Timer 6 | { 7 | public: 8 | RandomLFOComponent(RandomLFOParams params); 9 | ~RandomLFOComponent() override; 10 | 11 | void resized() override; 12 | void paint(juce::Graphics& g) override; 13 | 14 | std::function stateCallback; 15 | 16 | private: 17 | // Timer callback for updating the curve 18 | void timerCallback() override; 19 | 20 | // Generates a random value between -1 and 1 21 | float generateRandomValue(); 22 | 23 | // Converts a value in the range [-1, 1] to a Y-coordinate 24 | float valueToY(float value); 25 | 26 | // Creates the path for the waveform using the curve buffer 27 | void createPath(); 28 | 29 | std::vector curveBuffer; 30 | size_t bufferIndex = 0; 31 | size_t bufferSize = 0; 32 | juce::Path path; 33 | juce::Random random; 34 | RandomLFOParams params; 35 | 36 | int scalingFactor = 2; 37 | 38 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RandomLFOComponent) 39 | }; 40 | -------------------------------------------------------------------------------- /plugin/Source/util/StereoMSEGWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "StereoMSEGWrapper.h" 2 | 3 | void StereoMSEGWrapper::prepare(const juce::dsp::ProcessSpec& spec) 4 | { 5 | left.setSampleRate(spec.sampleRate); 6 | right.setSampleRate(spec.sampleRate); 7 | left.reset(); 8 | right.reset(); 9 | } 10 | 11 | void StereoMSEGWrapper::reset() 12 | { 13 | left.reset(); 14 | right.reset(); 15 | } 16 | 17 | void StereoMSEGWrapper::noteOn(float phase) 18 | { 19 | left.noteOn(phase); 20 | } 21 | 22 | void StereoMSEGWrapper::process(int numSamples) 23 | { 24 | left.process(numSamples); 25 | right.process(numSamples); 26 | } 27 | 28 | float StereoMSEGWrapper::getOutput(int channel) 29 | { 30 | jassert(channel == 0 || channel == 1); 31 | return channel == 0 ? left.getOutput() : right.getOutput(); 32 | } 33 | 34 | float StereoMSEGWrapper::getOutputUnclamped(int channel) 35 | { 36 | jassertfalse; 37 | return -1; 38 | } 39 | 40 | float StereoMSEGWrapper::getCurrentPhase(int channel) 41 | { 42 | jassert(channel == 0 || channel == 1); 43 | return channel == 0 ? left.getCurrentPhase() : right.getCurrentPhase(); 44 | } 45 | -------------------------------------------------------------------------------- /plugin/Resources/resonarium_logo_svg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugin/Source/ResonatorSynth.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Gabriel Soule on 5/1/24. 3 | // 4 | 5 | #ifndef RESONATORSYNTH_H 6 | #define RESONATORSYNTH_H 7 | 8 | #pragma once 9 | #include "defines.h" 10 | 11 | #include "GlobalState.h" 12 | #include "Parameters.h" 13 | #include "dsp/ResonariumEffectChain.h" 14 | #include "util/RandomLFO.h" 15 | #include "util/StereoLFOWrapper.h" 16 | #include "util/StereoMSEGWrapper.h" 17 | 18 | class ResonatorSynth : public gin::Synthesiser 19 | { 20 | public: 21 | ResonatorSynth(GlobalState& state, SynthParams params); 22 | void prepare(const juce::dsp::ProcessSpec& spec); 23 | void updateParameters(); 24 | void renderNextSubBlock(juce::AudioBuffer& outputAudio, int startSample, int numSamples) override; 25 | void panic(); 26 | 27 | GlobalState& state; 28 | SynthParams params; 29 | ResonariumEffectChain effectChain; 30 | StereoLFOWrapper monoLFOs[NUM_LFOS]; 31 | RandomLFO monoRandomLFOs[NUM_RANDOMS]; 32 | juce::Array monoMSEGs; 33 | juce::Array msegData; 34 | 35 | int currentBlockSize = -1; 36 | }; 37 | 38 | 39 | #endif //RESONATORSYNTH_H 40 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "modules/juce"] 2 | path = modules/juce 3 | url = https://github.com/juce-framework/JUCE.git 4 | branch = master 5 | [submodule "modules/gin"] 6 | path = modules/gin 7 | url = https://github.com/gabrielsoule/Gin.git 8 | branch = master 9 | [submodule "modules/melatonin_inspector"] 10 | path = modules/melatonin_inspector 11 | url = https://github.com/sudara/melatonin_inspector.git 12 | branch = main 13 | [submodule "modules/chowdsp_utils"] 14 | path = modules/chowdsp_utils 15 | url = https://github.com/gabrielsoule/chowdsp_utils.git 16 | branch = master 17 | [submodule "modules/melatonin_perfetto"] 18 | path = modules/melatonin_perfetto 19 | url = https://github.com/sudara/melatonin_perfetto.git 20 | branch = main 21 | [submodule "modules/melatonin_blur"] 22 | path = modules/melatonin_blur 23 | url = https://github.com/sudara/melatonin_blur.git 24 | branch = main 25 | [submodule "modules/pybind11"] 26 | path = modules/pybind11 27 | url = https://github.com/pybind/pybind11.git 28 | branch = master -------------------------------------------------------------------------------- /plugin/Source/util/StereoLFOWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "StereoLFOWrapper.h" 2 | 3 | void StereoLFOWrapper::prepare(const juce::dsp::ProcessSpec& spec) 4 | { 5 | left.setSampleRate(spec.sampleRate); 6 | right.setSampleRate(spec.sampleRate); 7 | } 8 | 9 | void StereoLFOWrapper::reset() 10 | { 11 | left.reset(); 12 | right.reset(); 13 | } 14 | 15 | void StereoLFOWrapper::noteOn(float phase = -1) 16 | { 17 | left.noteOn(phase); 18 | right.noteOn(phase); 19 | } 20 | 21 | void StereoLFOWrapper::process(int numSamples) 22 | { 23 | left.process(numSamples); 24 | right.process(numSamples); 25 | } 26 | 27 | float StereoLFOWrapper::getOutput(int channel) 28 | { 29 | jassert(channel == 0 || channel == 1); 30 | return channel == 0 ? left.getOutput() : right.getOutput(); 31 | } 32 | 33 | float StereoLFOWrapper::getOutputUnclamped(int channel) 34 | { 35 | jassert(channel == 0 || channel == 1); 36 | return channel == 0 ? left.getOutputUnclamped() : right.getOutputUnclamped(); 37 | } 38 | 39 | float StereoLFOWrapper::getCurrentPhase(int channel) 40 | { 41 | jassert(channel == 0 || channel == 1); 42 | return channel == 0 ? left.getCurrentPhase() : right.getCurrentPhase(); 43 | } 44 | -------------------------------------------------------------------------------- /plugin/Source/util/WrappedEnvelope.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Gabriel Soule on 7/17/24. 3 | // 4 | 5 | #ifndef ENVELOPE_H 6 | #define ENVELOPE_H 7 | 8 | #include "../Parameters.h" 9 | 10 | class WrappedEnvelope { 11 | public: 12 | 13 | void prepare(const juce::dsp::ProcessSpec& spec) 14 | { 15 | internalADSR.setSampleRate(spec.sampleRate); 16 | } 17 | 18 | template 19 | void updateParameters(T& source) 20 | { 21 | internalADSR.setAttack(source.getValue(params.attack)); 22 | internalADSR.setDecay(source.getValue(params.decay)); 23 | internalADSR.setSustainLevel(source.getValue(params.sustain)); 24 | internalADSR.setRelease(source.getValue(params.release)); 25 | } 26 | 27 | void noteOn() 28 | { 29 | internalADSR.noteOn(); 30 | } 31 | 32 | void noteOff() 33 | { 34 | internalADSR.noteOff(); 35 | } 36 | 37 | void reset() 38 | { 39 | internalADSR.reset(); 40 | } 41 | 42 | void process(int numSamples) 43 | { 44 | internalADSR.process(numSamples); 45 | } 46 | 47 | float getOutput() 48 | { 49 | return internalADSR.getOutput(); 50 | } 51 | 52 | ADSRParams params; 53 | gin::ADSR internalADSR; 54 | }; 55 | 56 | 57 | 58 | #endif //ENVELOPE_H 59 | -------------------------------------------------------------------------------- /plugin/Source/dsp/Filters.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Gabriel Soule on 5/20/24. 3 | // 4 | 5 | #include "Filters.h" 6 | 7 | float DispersionFilter::processSample(float input) 8 | { 9 | state[0] = c * input - s * state[1]; 10 | const auto output = s * input + c * state[1]; 11 | state[1] = state[0]; 12 | return output; 13 | } 14 | 15 | void DispersionFilter::prepare(juce::dsp::ProcessSpec spec) 16 | { 17 | reset(); 18 | } 19 | 20 | void DispersionFilter::reset() 21 | { 22 | state[0] = 0; 23 | state[1] = 0; 24 | } 25 | 26 | /** 27 | * Sets the amount of dispersion to apply to the signal (when placed inside a waveguide loop). 28 | * 0 = no dispersion, 1 = maximum dispersion, i.e. a negative polarity comb filter. 29 | */ 30 | void DispersionFilter::setDispersionAmount(float amount) 31 | { 32 | amount = juce::jlimit(0.0f, 1.0f, amount); 33 | float angle = amount * -juce::MathConstants::halfPi; 34 | c = std::cos(angle); 35 | s = std::sin(angle); 36 | // c = 1 - amount; 37 | // s = - amount; 38 | } 39 | 40 | float OneZeroFilter::processSample(float input) 41 | { 42 | const float output = (1 - p) * input + p * state; 43 | state = output; 44 | return output; 45 | } 46 | 47 | void OneZeroFilter::prepare(juce::dsp::ProcessSpec spec) 48 | { 49 | reset(); 50 | } 51 | 52 | void OneZeroFilter::reset() 53 | { 54 | state = 0; 55 | } 56 | 57 | void OneZeroFilter::setBrightness(float brightness) 58 | { 59 | this->p = brightness; 60 | } 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /plugin/Source/dsp/ResonariumEffectChain.h: -------------------------------------------------------------------------------- 1 | #ifndef RESONARIUMEFFECTCHAIN_H 2 | #define RESONARIUMEFFECTCHAIN_H 3 | 4 | #include "Distortion.h" 5 | #include "MultiAmp.h" 6 | #include "MultiDelay.h" 7 | #include "MVerb.h" 8 | #include "WrappedSVF.h" 9 | 10 | class ResonariumEffectChain 11 | { 12 | public: 13 | explicit ResonariumEffectChain(EffectChainParams params); 14 | 15 | void prepare(const juce::dsp::ProcessSpec& spec); 16 | 17 | void reset(); 18 | 19 | template 20 | void updateParameters(T& source, juce::AudioPlayHead* playhead); 21 | 22 | void process(juce::dsp::AudioBlock block) noexcept; 23 | 24 | int channel = 0; 25 | 26 | juce::dsp::Chorus chorus; 27 | ChorusParams chorusParams; 28 | 29 | MultiDelay delay; 30 | DelayParams delayParams; 31 | 32 | Distortion distortion; 33 | DistortionParams distortionParams; 34 | 35 | MultiAmp multiAmp; 36 | MultiAmpParams multiAmpParams; 37 | 38 | juce::dsp::Phaser phaser; 39 | PhaserParams phaserParams; 40 | 41 | juce::dsp::Compressor compressor; 42 | CompressorParams compressorParams; 43 | 44 | MVerb mverb; 45 | ReverbParams reverbParams; 46 | 47 | WrappedSVF filter1; 48 | WrappedSVF filter2; 49 | SVFParams filter1Params; 50 | SVFParams filter2Params; 51 | 52 | juce::dsp::Gain gain; 53 | 54 | EffectChainParams effectChainParams; 55 | double sampleRate = -1; 56 | }; 57 | 58 | #endif //RESONARIUMEFFECTCHAIN_H -------------------------------------------------------------------------------- /plugin/Source/util/StereoMSEGWrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef STEREOMSEGWRAPPER_H 2 | #define STEREOMSEGWRAPPER_H 3 | 4 | #include "../Parameters.h" 5 | 6 | class StereoMSEGWrapper 7 | { 8 | public: 9 | StereoMSEGWrapper(MSEGParams params) : dataPointer(params.msegData), left(*dataPointer), right(*dataPointer), params(params) 10 | { 11 | 12 | } 13 | 14 | void prepare(const juce::dsp::ProcessSpec& spec); 15 | 16 | template 17 | void updateParameters(T& source, float frequency) 18 | { 19 | this->ginParams.frequency = source.getValue(params.rate); 20 | this->ginParams.phase = source.getValue(params.phase); 21 | this->ginParams.offset = source.getValue(params.offset); 22 | this->ginParams.depth = source.getValue(params.depth); 23 | // this->ginParams.delay = source.getValue(params.delay); 24 | this->ginParams.fade = source.getValue(params.fade); 25 | this->ginParams.loop = true; 26 | 27 | left.setParameters(ginParams); 28 | right.setParameters(ginParams); 29 | } 30 | 31 | void reset(); 32 | void noteOn(float phase = -1); 33 | void process(int numSamples); 34 | float getOutput(int channel); 35 | float getOutputUnclamped(int channel); 36 | float getCurrentPhase(int channel); 37 | 38 | std::shared_ptr dataPointer; 39 | gin::MSEG left; 40 | gin::MSEG right; 41 | MSEGParams params; 42 | gin::MSEG::Parameters ginParams; 43 | 44 | float phase = 0.0f; 45 | float offset = 0.0f; 46 | float depth = 1.0f; 47 | float delay = 0.0f; 48 | float fade = 0.0f; 49 | float stereo = 0.0f; 50 | }; 51 | 52 | 53 | #endif //STEREOMSEGWRAPPER_H 54 | -------------------------------------------------------------------------------- /plugin/Source/ui/SettingsPanel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include // For applyStackBlur 6 | #include "../PluginProcessor.h" 7 | #include "ResonariumLookAndFeel.h" 8 | 9 | class SettingsPanel : public juce::Component 10 | { 11 | public: 12 | SettingsPanel(ResonariumProcessor& processor, juce::Component* parent); 13 | ~SettingsPanel() override; 14 | 15 | void paint(juce::Graphics& g) override; 16 | void resized() override; 17 | 18 | void show(); 19 | void hide(); 20 | 21 | // Close button click handler 22 | std::function onCloseButtonClick; 23 | 24 | private: 25 | ResonariumProcessor& proc; 26 | juce::Component* parentComponent; 27 | 28 | juce::ComboBox themeSelector; 29 | juce::Label themeLabel; 30 | juce::ToggleButton showTooltipsToggle; 31 | juce::ToggleButton enableAnimationsToggle; 32 | juce::Slider uiScaleSlider; 33 | juce::Label uiScaleLabel; 34 | juce::TextButton closeButton{"Close"}; 35 | 36 | // BlurryComp from gin::PluginAlertWindow 37 | class BlurryComp : public juce::Component 38 | { 39 | public: 40 | BlurryComp(juce::Image img) : background(img) 41 | { 42 | // Use the global function directly from gin 43 | gin::applyStackBlur(img, 4); 44 | } 45 | 46 | void paint(juce::Graphics& g) override 47 | { 48 | g.drawImage(background, getLocalBounds().toFloat()); 49 | } 50 | 51 | juce::Image background; 52 | }; 53 | 54 | std::unique_ptr blur; 55 | 56 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SettingsPanel) 57 | }; -------------------------------------------------------------------------------- /plugin/Source/ui/SampleDropperComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../dsp/Sampler.h" 4 | #include 5 | #include 6 | 7 | class SampleDropperComponent : public juce::Component, 8 | public juce::FileDragAndDropTarget, 9 | public juce::DragAndDropContainer, 10 | public juce::ChangeListener, 11 | public juce::SettableTooltipClient// Add this to listen for thumbnail changes 12 | { 13 | public: 14 | explicit SampleDropperComponent(Sampler& sampler); 15 | ~SampleDropperComponent() override; 16 | 17 | void paint(juce::Graphics& g) override; 18 | void resized() override; 19 | void mouseDown(const juce::MouseEvent& event) override; 20 | void changeListenerCallback(juce::ChangeBroadcaster* source) override; 21 | void lookAndFeelChanged() override; 22 | void updateFromSampler(); 23 | bool isInterestedInFileDrag(const juce::StringArray& files) override; 24 | void filesDropped(const juce::StringArray& files, int x, int y) override; 25 | void loadFile(const juce::File& file); 26 | void clearCurrentFile(); 27 | 28 | std::function onSampleLoaded; 29 | 30 | private: 31 | void updateLabels(); 32 | 33 | Sampler& sampler; 34 | juce::String currentFileName = ""; 35 | bool isFileLoaded = false; 36 | bool isBeingDraggedOver = false; 37 | 38 | juce::Label emptyStateLabel; 39 | juce::Label loadedStateLabel; 40 | 41 | juce::AudioThumbnailCache thumbnailCache; // Add this 42 | juce::AudioThumbnail thumbnail; // Add this 43 | 44 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SampleDropperComponent) 45 | }; 46 | -------------------------------------------------------------------------------- /plugin/Source/defines.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFINES_H 2 | #define DEFINES_H 3 | 4 | //everyone's favorite header file... a list of magic numbers! 5 | //At least they're all in one place and not scattered throughout the codebase like rice at a wedding. 6 | 7 | // Define M_PI for Windows builds where it's not available 8 | #ifndef M_PI 9 | #define M_PI 3.14159265358979323846 10 | #endif 11 | 12 | #define NUM_SYNTH_VOICES 16 13 | 14 | //resonators 15 | #define NUM_RESONATORS 8 16 | #define NUM_RESONATOR_BANKS 4 17 | 18 | //parameter values 19 | #define MIN_FILTER_FREQUENCY 10.0f 20 | #define MAX_FILTER_FREQUENCY 20000.0f 21 | #define FREQUENCY_KNOB_SKEW 0.3f 22 | 23 | //exciters 24 | #define NUM_IMPULSE_EXCITERS 1 25 | #define NUM_NOISE_EXCITERS 1 26 | #define NUM_IMPULSE_TRAIN_EXCITERS 1 27 | #define NUM_SAMPLE_EXCITERS 1 28 | 29 | //modulation sources 30 | #define NUM_LFOS 4 31 | #define NUM_ENVELOPES 4 32 | #define NUM_RANDOMS 4 33 | #define NUM_MSEGS 4 34 | #define NUM_MACROS 6 35 | 36 | //effects 37 | #define MAX_DELAY_IN_SECONDS 2.0f 38 | 39 | //misc. UI stuff 40 | #define WINDOW_WIDTH (1300 - 18) 41 | #define WINDOW_HEIGHT (900 - 11) 42 | #define BOX_HEADER_HEIGHT 28 43 | #define TOP_MENU_BAR_HEIGHT 40 44 | #define TITLE_BAR_HEIGHT 28 45 | #define PARAM_BOX_XSMALL_HEIGHT 105 46 | #define PARAM_BOX_SMALL_HEIGHT 170 47 | #define PARAM_BOX_MEDIUM_HEIGHT 235 48 | #define PARAM_BOX_LARGE_HEIGHT 300 49 | #define KNOB_W_XSMALL 25 50 | #define KNOB_H_XSMALL 32 51 | #define KNOB_W_SMALL 42 52 | #define KNOB_H_SMALL 57 53 | #define KNOB_W 52 54 | #define KNOB_H 65 55 | #define EXCITER_BOX_WIDTH (4 * KNOB_W + 4 + 4) 56 | #define MODULATION_BOX_WIDTH (8 * KNOB_W + 4 + 4) 57 | #define RESONATOR_BANK_BOX_WIDTH (2 * MODULATION_BOX_WIDTH) 58 | #define RESONATOR_BANK_BOX_HEIGHT (405 + 65 + 65) 59 | 60 | #endif //DEFINES_H 61 | -------------------------------------------------------------------------------- /plugin/Source/util/StereoLFOWrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef STEREOLFOWRAPPER_H 2 | #define STEREOLFOWRAPPER_H 3 | 4 | #include "../Parameters.h" 5 | /** 6 | * This class wraps around (two copies of) Gin's excellent LFO to provide stereo functionality. 7 | 8 | */ 9 | class StereoLFOWrapper 10 | { 11 | public: 12 | StereoLFOWrapper() = default; 13 | 14 | void prepare(const juce::dsp::ProcessSpec& spec); 15 | 16 | template 17 | void updateParameters(T& source, float frequency) 18 | { 19 | this->ginParams.frequency = frequency; 20 | this->ginParams.waveShape = static_cast(params.wave->getProcValue()); 21 | this->ginParams.phase = source.getValue(params.phase); 22 | this->ginParams.offset = source.getValue(params.offset); 23 | this->ginParams.depth = source.getValue(params.depth); 24 | this->ginParams.delay = source.getValue(params.delay); 25 | this->ginParams.fade = source.getValue(params.fade); 26 | 27 | left.setParameters(ginParams); 28 | ginParams.phase += source.getValue(params.stereo); 29 | right.setParameters(ginParams); 30 | } 31 | 32 | void reset(); 33 | void noteOn(float phase); 34 | void process(int numSamples); 35 | float getOutput(int channel); 36 | float getOutputUnclamped(int channel); 37 | float getCurrentPhase(int channel); 38 | 39 | gin::LFO left; 40 | gin::LFO right; 41 | LFOParams params; 42 | gin::LFO::Parameters ginParams; 43 | 44 | gin::LFO::WaveShape waveShape = gin::LFO::WaveShape::sine; 45 | float frequency = 1.0f; 46 | float phase = 0.0f; 47 | float offset = 0.0f; 48 | float depth = 1.0f; 49 | float delay = 0.0f; 50 | float fade = 0.0f; 51 | float stereo = 0.0f; 52 | }; 53 | 54 | 55 | #endif //STEREOLFOWRAPPER_H 56 | -------------------------------------------------------------------------------- /plugin/Source/GlobalState.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBALSTATE_H 2 | #define GLOBALSTATE_H 3 | 4 | #include 5 | #include "defines.h" 6 | #include "dsp/Sampler.h" 7 | 8 | /** 9 | (A reference to) this struct is passed down to all components that require global state information, 10 | such as global buffers, parameters, and modulation matrices. 11 | 12 | We do not want low-level components to reach up the tree into the plugin processor to get important information. 13 | Such behavior often induces circular dependencies, and it's generally bad practice. 14 | */ 15 | class GlobalState 16 | { 17 | public: 18 | GlobalState() = default; 19 | ~GlobalState() = default; 20 | gin::ModMatrix modMatrix; 21 | gin::ModSrcId modSrcPressure; 22 | gin::ModSrcId modSrcTimbre; 23 | gin::ModSrcId modSrcPitchbend; 24 | gin::ModSrcId modSrcNote; 25 | gin::ModSrcId modSrcVelocity; 26 | gin::ModSrcId modSrcFrequency; 27 | std::array modSrcMonoLFO; 28 | std::array modSrcPolyLFO; 29 | std::array modSrcMonoRND; 30 | std::array modSrcPolyRND; 31 | std::array modSrcPolyENV; 32 | std::array modSrcMonoMSEG; 33 | std::array modSrcPolyMSEG; 34 | std::array modSrcCC; 35 | std::array modSrcMacro; 36 | Sampler sampler; 37 | juce::String samplePath = ""; 38 | juce::String logPrefix = ""; 39 | bool bypassResonators = false; 40 | bool soloActive = false; 41 | int soloBankIndex = 0; 42 | int soloResonatorIndex = 0; 43 | bool polyFX = false; 44 | juce::AudioPlayHead* playHead; 45 | juce::AudioBuffer extInputBuffer; 46 | }; 47 | 48 | 49 | #endif //GLOBALSTATE_H 50 | -------------------------------------------------------------------------------- /plugin/Source/dsp/Distortion.h: -------------------------------------------------------------------------------- 1 | #ifndef DISTORTION_H 2 | #define DISTORTION_H 3 | #include "../Parameters.h" 4 | #include 5 | 6 | class Distortion 7 | { 8 | public: 9 | enum DistortionMode 10 | { 11 | SOFT_CLIP, 12 | HARD_CLIP, 13 | LINEAR_FOLD, 14 | SIN_FOLD, 15 | BIT_CRUSH, 16 | DOWN_SAMPLE, 17 | NUM_MODES 18 | }; 19 | 20 | enum FilterMode 21 | { 22 | disabled, 23 | pre, 24 | post 25 | }; 26 | 27 | explicit Distortion(DistortionParams params); 28 | 29 | void prepare(const juce::dsp::ProcessSpec& spec); 30 | 31 | void reset(); 32 | 33 | template 34 | void updateParameters(T& source) 35 | { 36 | distortionMode = static_cast(params.distortionMode->getProcValue()); 37 | drive[0] = source.getValue(params.drive, 0); 38 | drive[1] = source.getValue(params.drive, 1); 39 | outputGain[0] = source.getValue(params.outputGain, 0); 40 | outputGain[1] = source.getValue(params.outputGain, 1); 41 | filterMode = static_cast(params.filterMode->getProcValue()); 42 | filter.updateParameters(source.getValue(params.cutoff), source.getValue(params.resonance), 43 | source.getValue(params.filterMode)); 44 | } 45 | 46 | void process(juce::dsp::ProcessContextReplacing context); 47 | 48 | private: 49 | DistortionParams params; 50 | 51 | DistortionMode distortionMode; 52 | float drive[2] = {1.0f, 1.0f}; 53 | float outputGain[2] = {1.0f, 1.0f}; 54 | FilterMode filterMode = disabled; 55 | chowdsp::SVFMultiMode filter; 56 | 57 | // State variables for downsampling 58 | float lastDownSampleValue = 0.0f; 59 | float downSampleCounter = 0.0f; 60 | }; 61 | 62 | #endif // DISTORTION_H 63 | -------------------------------------------------------------------------------- /plugin/Source/dsp/WrappedSVF.h: -------------------------------------------------------------------------------- 1 | #ifndef WRAPPEDSVF_H 2 | #define WRAPPEDSVF_H 3 | 4 | #include "../Parameters.h" 5 | #include 6 | /** 7 | * Wraps a state variable filter in a convenient class that contains its own parameters struct, 8 | * along with a simple method for updating the filter coefficients. 9 | * 10 | * TODO Add stereo modulation support 11 | */ 12 | class WrappedSVF 13 | { 14 | public: 15 | explicit WrappedSVF(SVFParams params) : params(params) {} 16 | 17 | void prepare(const juce::dsp::ProcessSpec& spec); 18 | void reset(); 19 | 20 | template 21 | void updateParameters(T& source) 22 | { 23 | const float cutoffL = source.getValue(params.cutoff, 0); 24 | const float resonanceL = source.getValue(params.resonance, 0); 25 | const float modeL = source.getValue(params.mode, 0); 26 | const float cutoffR = source.getValue(params.cutoff, 1); 27 | const float resonanceR = source.getValue(params.resonance, 1); 28 | const float modeR = source.getValue(params.mode, 1); 29 | svfL.updateParameters(cutoffL, resonanceL, modeL); 30 | svfR.updateParameters(cutoffR, resonanceR, modeR); 31 | } 32 | 33 | template 34 | void process(ProcessContext& context) 35 | { 36 | juce::dsp::AudioBlock blockL = context.getOutputBlock().getSingleChannelBlock(0); 37 | juce::dsp::AudioBlock blockR = context.getOutputBlock().getSingleChannelBlock(1); 38 | juce::dsp::ProcessContextReplacing contextL(blockL); 39 | juce::dsp::ProcessContextReplacing contextR(blockR); 40 | svfL.process(contextL); 41 | svfR.process(contextR); 42 | } 43 | 44 | chowdsp::SVFMultiMode svfL; 45 | chowdsp::SVFMultiMode svfR; 46 | SVFParams params; 47 | }; 48 | 49 | 50 | #endif //WRAPPEDSVF_H 51 | -------------------------------------------------------------------------------- /plugin/Source/util/ResonariumUtilities.cpp: -------------------------------------------------------------------------------- 1 | #include "ResonariumUtilities.h" 2 | #include 3 | #include 4 | 5 | int ResonariumUtilities::calculateMPEPitchBendForFrequency(float targetFreq, 6 | int currentNoteNumber, 7 | float pitchBendRange) 8 | { 9 | float targetNoteNumber = 69.0f + 12.0f * std::log2(targetFreq / 440.0f); 10 | float semitonesDiff = targetNoteNumber - currentNoteNumber; 11 | 12 | // Convert to pitch bend value (assuming ±48 semitone range) 13 | // Scale factor is (16383/96) since the full range is 96 semitones (-48 to +48) 14 | // and the full pitch bend range is 0 to 16383 15 | int pitchBendValue = 8192 + static_cast( 16 | round(semitonesDiff * (16383.0f / (pitchBendRange * 2.0f)))); 17 | 18 | // Clamp to valid range (0-16383) 19 | return std::clamp(pitchBendValue, 0, 16383); 20 | } 21 | 22 | bool ResonariumUtilities::saveComponentToImage(juce::Component& comp, 23 | const juce::File& file, 24 | float scaleFactor) 25 | { 26 | auto bounds = comp.getLocalBounds(); 27 | if (auto* format = juce::ImageFileFormat::findImageFormatForFileExtension(file)) 28 | { 29 | juce::FileOutputStream out(file); 30 | 31 | if (out.openedOk()) 32 | { 33 | auto snapshot = comp.createComponentSnapshot(bounds, true, scaleFactor); 34 | DBG("Rendering component to " + file.getFullPathName()); 35 | return format->writeImageToStream(snapshot, out); 36 | } else 37 | { 38 | DBG ("Failed to open file for writing: " + out.getStatus().getErrorMessage()); 39 | return false; 40 | } 41 | } 42 | DBG("Failed to render component to " + file.getFullPathName()); 43 | return false; 44 | } 45 | -------------------------------------------------------------------------------- /plugin/Source/WaveguideResonatorBank.h: -------------------------------------------------------------------------------- 1 | #ifndef WAVEGUIDERESONATORBANK_H 2 | #define WAVEGUIDERESONATORBANK_H 3 | #include "defines.h" 4 | #include "GlobalState.h" 5 | #include "ResonatorBank.h" 6 | #include "StereoResonator.h" 7 | 8 | class ResonatorVoice; 9 | /** 10 | * A bank of several Resonators, with support for different intra-resonator feedback modes. 11 | */ 12 | class WaveguideResonatorBank 13 | { 14 | public: 15 | enum CouplingMode 16 | { 17 | PARALLEL, 18 | INTERLINKED, 19 | CASCADE, 20 | }; 21 | 22 | WaveguideResonatorBank(GlobalState& state, ResonatorVoice& parentVoice, WaveguideResonatorBankParams params); 23 | ~WaveguideResonatorBank(); 24 | void process( 25 | juce::dsp::AudioBlock& exciterBlock, 26 | juce::dsp::AudioBlock & previousResonatorBankBlock); 27 | void reset(); 28 | void prepare(const juce::dsp::ProcessSpec& spec); 29 | void updateParameters(float newFrequency, int numSamples); 30 | void setFeedbackMode(CouplingMode newMode); 31 | 32 | GlobalState& state; 33 | ResonatorVoice& voice; 34 | WaveguideResonatorBankParams params; 35 | int index = -1; 36 | CouplingMode couplingMode = PARALLEL; 37 | float frequency = 44100.0f; 38 | float sampleRate = 440.0f; 39 | juce::OwnedArray resonators; 40 | 41 | float previousResonatorBankMix = 0.0f; //how much of the previous resonator bank's output should we mix in? 42 | float exciterMix = 1.0f; //how much of the exciter signal should we mix in? 43 | float inputGain = 1.0f; 44 | float outputGain = 1.0f; 45 | 46 | //below is for cascade mode 47 | juce::dsp::IIR::Coefficients::Ptr dcBlockerCoefficients; 48 | juce::dsp::IIR::Filter dcBlockersL[NUM_RESONATORS]; 49 | juce::dsp::IIR::Filter dcBlockersR[NUM_RESONATORS]; 50 | chowdsp::SVFMultiMode cascadeFilterL; 51 | chowdsp::SVFMultiMode cascadeFilterR; 52 | 53 | chowdsp::SVFMultiMode testInterlinkedFilterL; 54 | chowdsp::SVFMultiMode testInterlinkedFilterR; 55 | }; 56 | 57 | #endif //WAVEGUIDERESONATORBANK_H 58 | -------------------------------------------------------------------------------- /plugin/Source/ui/ResonariumLookAndFeel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | //Code adapted from gin::PluginLookAndFeel 5 | class ResonariumLookAndFeel : public gin::PluginLookAndFeel 6 | { 7 | public: 8 | ResonariumLookAndFeel(); 9 | 10 | juce::Typeface::Ptr getTypefaceForFont(const juce::Font& f) override; 11 | 12 | void drawRotarySlider(juce::Graphics&, int x, int y, int width, int height, 13 | float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, 14 | juce::Slider&) override; 15 | 16 | void drawLinearSlider(juce::Graphics&, int x, int y, int width, int height, 17 | float sliderPos, float minSliderPos, float maxSliderPos, 18 | const juce::Slider::SliderStyle, juce::Slider&) override; 19 | 20 | 21 | void drawButtonBackground(juce::Graphics&, juce::Button&, const juce::Colour& backgroundColour, 22 | bool isMouseOverButton, bool isButtonDown) override; 23 | 24 | void drawButtonText(juce::Graphics&, juce::TextButton&, bool isMouseOverButton, bool isButtonDown) override; 25 | 26 | void drawComboBox(juce::Graphics&, int width, int height, bool isButtonDown, 27 | int buttonX, int buttonY, int buttonW, int buttonH, 28 | juce::ComboBox&) override; 29 | 30 | void drawTooltip(juce::Graphics& g, const juce::String& text, int width, int height) override; 31 | 32 | void positionComboBoxText(juce::ComboBox&, juce::Label&) override; 33 | 34 | void drawTextEditorOutline(juce::Graphics&, int width, int height, juce::TextEditor&) override; 35 | 36 | juce::PopupMenu::Options getOptionsForComboBoxPopupMenu(juce::ComboBox& box, juce::Label&) override; 37 | 38 | int getAlertWindowButtonHeight() override { return 20; } 39 | juce::Font getLabelFont(juce::Label&) override; 40 | juce::Font getTextButtonFont(juce::TextButton&, int buttonHeight) override; 41 | juce::Font getPopupMenuFont() override; 42 | 43 | juce::SharedResourcePointer images; 44 | juce::Typeface::Ptr typeface; 45 | juce::FontOptions defaultFont; 46 | }; 47 | 48 | class ResonariumLookAndFeelWrapper : public ResonariumLookAndFeel 49 | { 50 | public: 51 | ResonariumLookAndFeelWrapper(); 52 | ~ResonariumLookAndFeelWrapper(); 53 | }; 54 | -------------------------------------------------------------------------------- /plugin/Source/ui/RandomLFOComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "RandomLFOComponent.h" 2 | 3 | RandomLFOComponent::RandomLFOComponent(RandomLFOParams params) : params(params) 4 | { 5 | startTimerHz(20); 6 | } 7 | 8 | RandomLFOComponent::~RandomLFOComponent() 9 | { 10 | stopTimer(); 11 | } 12 | 13 | void RandomLFOComponent::resized() 14 | { 15 | auto area = getLocalBounds().reduced(2); 16 | bufferSize = area.getWidth() / scalingFactor; 17 | curveBuffer.resize(bufferSize, valueToY(0.0f)); 18 | 19 | bufferIndex = 0; 20 | createPath(); 21 | } 22 | 23 | void RandomLFOComponent::paint(juce::Graphics& g) 24 | { 25 | g.setColour(juce::Colours::black); 26 | g.fillRoundedRectangle(getLocalBounds().toFloat(), 10); 27 | const auto c = findColour (gin::GinLookAndFeel::accentColourId).withAlpha (isEnabled() ? 1.0f : 0.5f); 28 | g.setColour(c); 29 | g.strokePath(path, juce::PathStrokeType(2.0f)); 30 | } 31 | 32 | void RandomLFOComponent::timerCallback() 33 | { 34 | if(params.enabled->isOn() && stateCallback) 35 | { 36 | const float value = stateCallback() * 2.0f - 1.0f; 37 | curveBuffer[bufferIndex] = valueToY(value); 38 | bufferIndex = (bufferIndex + 1) % bufferSize; 39 | createPath(); 40 | repaint(); 41 | } 42 | } 43 | 44 | float RandomLFOComponent::generateRandomValue() 45 | { 46 | return random.nextFloat() * 2.0f - 1.0f; 47 | } 48 | 49 | float RandomLFOComponent::valueToY(float value) 50 | { 51 | auto area = getLocalBounds().reduced(2); 52 | return area.getBottom() - (value + 1.0f) / 2.0f * area.getHeight(); 53 | } 54 | 55 | void RandomLFOComponent::createPath() 56 | { 57 | path.clear(); 58 | 59 | if (curveBuffer.empty()) 60 | return; 61 | 62 | auto area = getLocalBounds().reduced(2); 63 | size_t numPoints = curveBuffer.size(); 64 | 65 | float xScale = static_cast(area.getWidth()) / static_cast(numPoints - 1); 66 | 67 | size_t index = bufferIndex; 68 | 69 | float x = 0.0f; 70 | float y = curveBuffer[index]; 71 | path.startNewSubPath(area.getX() + x, y); 72 | 73 | for (size_t i = 1; i < numPoints; ++i) 74 | { 75 | index = (index + 1) % numPoints; 76 | x = static_cast(i) * xScale; 77 | y = curveBuffer[index]; 78 | 79 | if (x > area.getWidth()) 80 | break; 81 | 82 | path.lineTo(area.getX() + x, y); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /plugin/Source/ui/ClickThruSelect.h: -------------------------------------------------------------------------------- 1 | #ifndef CLICKTHRUSELECT_H 2 | #define CLICKTHRUSELECT_H 3 | 4 | #include "ResonariumLookAndFeel.h" 5 | /** 6 | An select-like that allows for click-through selection. 7 | Designed to have the same layout and aesthetic as TextSlider.h 8 | */ 9 | class ClickThruSelect : public gin::ParamComponent, private gin::Parameter::ParameterListener { 10 | public: 11 | explicit ClickThruSelect(gin::Parameter* parameter, juce::Colour colour = juce::Colour()) : gin::ParamComponent(parameter), colour(colour) 12 | { 13 | if(colour != juce::Colour()) 14 | { 15 | label.setColour(juce::Label::textColourId, colour); 16 | customColor = true; 17 | } 18 | addAndMakeVisible(label); 19 | label.setText(parameter->getUserValueText(), juce::dontSendNotification); 20 | label.setInterceptsMouseClicks(false, false); 21 | parameter->addListener(this); 22 | 23 | } 24 | 25 | void resized() override 26 | { 27 | label.setBounds(this->getLocalBounds()); 28 | label.setJustificationType(juce::Justification::centred); 29 | label.setFont(label.getFont().withHeight(fontSize)); 30 | } 31 | 32 | void lookAndFeelChanged() override 33 | { 34 | if(!customColor) label.setColour(juce::Label::textColourId, getLookAndFeel().findColour(ResonariumLookAndFeel::accentColourId)); 35 | } 36 | 37 | void mouseDown(const juce::MouseEvent& event) override 38 | { 39 | int newValue = (static_cast(parameter->getUserValue()) + 1) % static_cast(parameter->getUserRangeEnd() + 1); 40 | label.setText(parameter->getUserValueText(), juce::dontSendNotification); 41 | parameter->setUserValue(newValue); 42 | 43 | } 44 | 45 | void enablementChanged() override 46 | { 47 | if(this->isEnabled()) 48 | { 49 | label.setColour(juce::Label::textColourId, colour); 50 | } else 51 | { 52 | label.setColour(juce::Label::textColourId, juce::Colours::darkgrey); 53 | } 54 | } 55 | 56 | void valueUpdated(gin::Parameter* param) override 57 | { 58 | label.setText(parameter->getUserValueText(), juce::dontSendNotification); 59 | } 60 | 61 | bool customColor = false; 62 | juce::Colour colour; 63 | juce::Label label; 64 | float fontSize = 21.0f; 65 | }; 66 | 67 | 68 | 69 | #endif //CLICKTHRUSELECT_H 70 | -------------------------------------------------------------------------------- /plugin/Source/util/RandomLFO.h: -------------------------------------------------------------------------------- 1 | #ifndef RANDOMLFO_H 2 | #define RANDOMLFO_H 3 | #include "../Parameters.h" 4 | 5 | /** 6 | * A versatile source of randomness that can be used as a modulator signal. 7 | * Produces either a monophonic or a polyphonic output. 8 | * Can be supplied with an optional RandomLFOParameters struct to control its behavior. 9 | */ 10 | class RandomLFO { 11 | //Based off of the excellent Perlin random generator in Matt Tytel's Vital synth 12 | static float perlinInterpolate(float from, float to, float t); 13 | struct RandomState 14 | { 15 | juce::Random rng; 16 | float lastRandomValue = 0; //the previous "step" in the random walk 17 | float nextRandomValue = 0; //the next "step" in the random walk 18 | float currentRandomValue = 0; //the current value at the end of the block of samples, interpolated between last and next 19 | std::atomic atomicState = 0; //used for atomic reads from the UI thread 20 | }; 21 | 22 | void processInternal(int numSamples, RandomState& state); 23 | 24 | public: 25 | 26 | enum Mode 27 | { 28 | SMOOTH, 29 | STEP, 30 | SAMPLE_AND_HOLD, 31 | }; 32 | RandomLFO() : leftState(sideStates[0]), rightState(sideStates[1]){} 33 | RandomLFO(gin::ModVoice* voice, bool stereo); 34 | RandomLFO(RandomLFOParams params, bool stereo); 35 | RandomLFO(gin::ModVoice* voice, RandomLFOParams params, bool stereo); 36 | void prepare(const juce::dsp::ProcessSpec& spec); 37 | void updateParameters(float frequency); 38 | void updateParametersMono(gin::ModMatrix& matrix, float frequency); 39 | /** 40 | * Returns the value of the random LFO after numSamples have been processed, 41 | * and updates its internal state accordingly. 42 | */ 43 | void reset(); 44 | void noteOn(float phase); 45 | void process(int numSamples); 46 | float getOutput(); 47 | float getOutput(int channel); 48 | 49 | gin::ModVoice* voice = nullptr; 50 | RandomLFOParams params; 51 | int numChannels; 52 | double sampleRate; 53 | float oneOverSampleRate; 54 | bool stereo = false; 55 | RandomState sideStates[2]; 56 | RandomState centerState; 57 | RandomState& leftState; //convenient aliases 58 | RandomState& rightState; 59 | 60 | float currentPhase; 61 | float phaseDelta; 62 | 63 | Mode mode = SMOOTH; 64 | bool sync = false; 65 | float rate = 1.0f; 66 | float depth = 1.0f; 67 | float offset = 0.0f; 68 | float smooth = 0.0f; 69 | float chaos = 1.0f; 70 | float jitter = 0.0f; 71 | float stereoAmount = 0.0f; 72 | }; 73 | 74 | 75 | 76 | #endif //RANDOMLFO_H 77 | -------------------------------------------------------------------------------- /plugin/Source/dsp/MultiFilter.h: -------------------------------------------------------------------------------- 1 | #ifndef MULTIFILTER_H 2 | #define MULTIFILTER_H 3 | 4 | #include "../Parameters.h" 5 | /** 6 | * Light wrapper over to easily enable multiple filter types in a single class, 7 | * along with an optional set of host-controlled parameters. 8 | * The MultiFilter is marginally clever: if the voice pointer 9 | * is null, it will operate in monophonic mode automatically, 10 | * and if any of the parameter pointers are null, then it will operate 11 | * without relying on the hosted parameters. It will operate in mono or stereo 12 | * depending on the number of channels in the input block. 13 | */ 14 | class MultiFilter 15 | { 16 | public: 17 | 18 | enum Type 19 | { 20 | none = 0, 21 | lowpass, 22 | highpass, 23 | bandpass, 24 | notch, 25 | allpass, 26 | }; 27 | 28 | MultiFilter() : type(none), normalize(false) {} 29 | MultiFilter(bool normalize) : type(none), normalize(normalize) {} 30 | MultiFilter(MultiFilterParams params, bool normalize) : type(none), params(params), normalize(normalize) 31 | { 32 | params.frequency = nullptr; 33 | params.resonance = nullptr; 34 | params.type = nullptr; 35 | } 36 | MultiFilter(gin::ModVoice* voice, MultiFilterParams params, bool normalize) : type(none), voice(voice), params(params), normalize(normalize) {} 37 | void prepare(const juce::dsp::ProcessSpec& spec); 38 | void reset(); 39 | void setType(Type type); 40 | void setParameters(float frequency, float q); 41 | static std::array makeHighGainBandpass(float sampleRate, float frequency, float Q); 42 | float processSample(int channel, float sample) noexcept; 43 | void process (juce::dsp::AudioBlock& block) noexcept; 44 | 45 | /** 46 | * Updates the filter coefficients automatically by querying the hosted parameter struct. 47 | */ 48 | void updateParameters(); 49 | 50 | // juce::dsp::ProcessorDuplicator, juce::dsp::IIR::Coefficients> filter; 51 | juce::dsp::IIR::Filter filterL; 52 | juce::dsp::IIR::Filter filterR; 53 | Type type; 54 | float freq; 55 | float Q; 56 | float sampleRate; 57 | bool poly; // true if the filter is a polyphonic filter, i.e. voice is not null 58 | bool useHostedParams; //true if the filter should retrieve parameters from the hosted parameter struct 59 | bool updateFlag = false; 60 | gin::ModVoice* voice; 61 | MultiFilterParams params; 62 | bool normalize; //true if the bandpass filter should be unity gain, or boosted by Q 63 | std::array coefficients; 64 | }; 65 | 66 | 67 | #endif //MULTIFILTER_H 68 | -------------------------------------------------------------------------------- /plugin/Source/dsp/MultiAmp.cpp: -------------------------------------------------------------------------------- 1 | #include "MultiAmp.h" 2 | 3 | MultiAmp::MultiAmp(std::function sampleRateCallback, MultiAmpParams params) : 4 | params(params), 5 | bassAmp(sampleRateCallback), 6 | bigAmp(sampleRateCallback), 7 | deRez2(sampleRateCallback), 8 | fireAmp(sampleRateCallback), 9 | grindAmp(sampleRateCallback), leadAmp(sampleRateCallback) 10 | { 11 | } 12 | 13 | void MultiAmp::reset() 14 | { 15 | bassAmp.reset(); 16 | bigAmp.reset(); 17 | deRez2.reset(); 18 | fireAmp.reset(); 19 | grindAmp.reset(); 20 | leadAmp.reset(); 21 | } 22 | 23 | juce::String MultiAmp::getParameterName(Mode mode, int index) 24 | { 25 | char* name = new char[32]; 26 | 27 | switch (mode) 28 | { 29 | case LEAD: 30 | LeadAmp::getParameterName(index, name); 31 | break; 32 | case FIRE: 33 | FireAmp::getParameterName(index, name); 34 | break; 35 | case GRIND: 36 | GrindAmp::getParameterName(index, name); 37 | break; 38 | case BIG: 39 | BigAmp::getParameterName(index, name); 40 | break; 41 | case DEREZ: 42 | DeRez2::getParameterName(index, name); 43 | break; 44 | case BASS: 45 | BassAmp::getParameterName(index, name); 46 | break; 47 | default: 48 | jassertfalse; 49 | } 50 | 51 | return juce::String(name, 32); 52 | } 53 | 54 | void MultiAmp::process(juce::dsp::ProcessContextReplacing context) 55 | { 56 | jassert(context.getOutputBlock().getNumSamples() == context.getInputBlock().getNumSamples()); 57 | float* channelL = context.getOutputBlock().getChannelPointer(0); 58 | float* channelR = context.getOutputBlock().getChannelPointer(1); 59 | float* channels[] = {channelL, channelR}; 60 | switch (mode) 61 | { 62 | case LEAD: 63 | leadAmp.processReplacing(channels, channels, context.getOutputBlock().getNumSamples()); 64 | break; 65 | case FIRE: 66 | fireAmp.processReplacing(channels, channels, context.getOutputBlock().getNumSamples()); 67 | break; 68 | case GRIND: 69 | grindAmp.processReplacing(channels, channels, context.getOutputBlock().getNumSamples()); 70 | break; 71 | case BIG: 72 | bigAmp.processReplacing(channels, channels, context.getOutputBlock().getNumSamples()); 73 | break; 74 | case DEREZ: 75 | deRez2.processReplacing(channels, channels, context.getOutputBlock().getNumSamples()); 76 | break; 77 | case BASS: 78 | bassAmp.processReplacing(channels, channels, context.getOutputBlock().getNumSamples()); 79 | break; 80 | default: 81 | jassertfalse; 82 | break; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /plugin/Source/util/InterpolatedValue.h: -------------------------------------------------------------------------------- 1 | #ifndef INTERPOLATEDVALUE_H 2 | #define INTERPOLATEDVALUE_H 3 | 4 | /** 5 | * Smoothly interpolates between two values of a gin::Parameter over a specified number of samples. 6 | * For use within a single block of processing. 7 | */ 8 | class InterpolatedValue 9 | { 10 | public: 11 | InterpolatedValue(): resetFlag(false) 12 | { 13 | } 14 | 15 | /** 16 | * Sets the target value of the InterpolatedParameter to the specified value, 17 | * using modulation from the specified source (either the monophonic ModMatrix or a polyphonic ModVoice). 18 | */ 19 | void setTargetValue(float targetValue, int numSamples) 20 | { 21 | jassert(numSamples >= 0); 22 | if (numSamples == 0) 23 | { 24 | numSamples = 1; 25 | } 26 | if(resetFlag) 27 | { 28 | snapValue(targetValue); 29 | } 30 | target = targetValue; 31 | noop = current == target; 32 | increment = (target - current) / static_cast(numSamples); 33 | samplesRemaining = numSamples; 34 | } 35 | 36 | /** 37 | * Snaps both the current and target values of the InterpolatedParameter to the specified source value, 38 | * and resets the filter state. 39 | */ 40 | void snapValue(float value) 41 | { 42 | current = value; 43 | target = value; 44 | filterState = value; 45 | } 46 | 47 | //Resets the state of the InterpolatedParameter. 48 | //When setTargetValue is called for the first time after a reset, 49 | //the InterpolatedParameter will snap to the target value. 50 | void reset() 51 | { 52 | resetFlag = true; 53 | } 54 | 55 | /** 56 | * Performs a single step of the interpolation process, returning the next value of the InterpolatedParameter. 57 | * This function should only be called numSamples times before calling setTargetValue() again. 58 | */ 59 | float nextValue() 60 | { 61 | jassert(samplesRemaining > 0); 62 | if (noop) return target; 63 | samplesRemaining--; 64 | current += increment; 65 | if (samplesRemaining == 0) 66 | { 67 | current = target; 68 | } 69 | return current; 70 | } 71 | 72 | private: 73 | inline float processOnePole(float input) 74 | { 75 | filterState = input + onePoleCoefficient * (filterState - input); 76 | return filterState; 77 | } 78 | 79 | int samplesRemaining = -1; 80 | float filterState = 0; 81 | float current = 0; 82 | float target = 0; 83 | float increment = 0; 84 | bool noop = false; 85 | bool resetFlag; 86 | 87 | float onePoleCoefficient = 44100.0f - 44.0f / 44100.0f; 88 | }; 89 | 90 | 91 | #endif //INTERPOLATEDVALUE_H 92 | -------------------------------------------------------------------------------- /plugin/Source/dsp/airwindows/DeRez2.h: -------------------------------------------------------------------------------- 1 | /* ======================================== 2 | * DeRez2 - DeRez2.h 3 | * Created 8/12/11 by SPIAdmin 4 | * Copyright (c) 2011 __MyCompanyName__, Airwindows uses the MIT license 5 | * ======================================== */ 6 | 7 | #pragma once 8 | 9 | #include "FXBase.h" 10 | #include 11 | #include 12 | #include 13 | 14 | class DeRez2 final : public AudioEffectX 15 | { 16 | public: 17 | enum { 18 | kParamA = 0, 19 | kParamB = 1, 20 | kParamC = 2, 21 | kParamD = 3, 22 | kNumParameters = 4 23 | }; // 24 | 25 | static constexpr int kNumPrograms = 0; 26 | static constexpr int kNumInputs = 2; 27 | static constexpr int kNumOutputs = 2; 28 | static constexpr unsigned long kUniqueId = 'eerz'; //Change this to what the AU identity is! 29 | 30 | DeRez2(audioMasterCallback audioMaster); 31 | ~DeRez2(); 32 | void reset(); 33 | bool getEffectName(char* name); // The plug-in name 34 | VstPlugCategory getPlugCategory(); // The general category for the plug-in 35 | bool getProductString(char* text); // This is a unique plug-in string provided by Steinberg 36 | bool getVendorString(char* text); // Vendor info 37 | VstInt32 getVendorVersion(); // Version number 38 | void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames); 39 | void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames); 40 | void getProgramName(char *name); // read the name from the host 41 | void setProgramName(char *name); // changes the name of the preset displayed in the host 42 | VstInt32 getChunk (void** data, bool isPreset); 43 | VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset); 44 | float getParameter(VstInt32 index); // get the parameter value at the specified index 45 | void setParameter(VstInt32 index, float value); // set the parameter at index to value 46 | void getParameterLabel(VstInt32 index, char *text); // label for the parameter (eg dB) 47 | static void getParameterName(VstInt32 index, char *text); // name of the parameter 48 | void getParameterDisplay(VstInt32 index, char *text); // text description of the current value 49 | VstInt32 canDo(char *text); 50 | private: 51 | char _programName[kVstMaxProgNameLen + 1]; 52 | std::set< std::string > _canDo; 53 | 54 | double lastSampleL; 55 | double heldSampleL; 56 | double lastDrySampleL; 57 | double lastOutputSampleL; 58 | 59 | double lastSampleR; 60 | double heldSampleR; 61 | double lastDrySampleR; 62 | double lastOutputSampleR; 63 | 64 | double position; 65 | double incrementA; 66 | double incrementB; 67 | 68 | uint32_t fpdL; 69 | uint32_t fpdR; 70 | //default stuff 71 | 72 | float A; 73 | float B; 74 | float C; 75 | float D; 76 | 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /plugin/Source/dsp/MultiDelay.cpp: -------------------------------------------------------------------------------- 1 | #include "MultiDelay.h" 2 | 3 | MultiDelay::MultiDelay(float maxDelayInSeconds) : 4 | maxDelayInSeconds(maxDelayInSeconds), 5 | delayLine(48000 * maxDelayInSeconds) 6 | { 7 | // Initialize default values 8 | pingPongEnabled = false; 9 | feedbackL = feedbackR = 0.5f; 10 | timeL = timeR = 0.5f; 11 | mixL = mixR = 0.5f; 12 | 13 | // Initialize smoothed values 14 | delayTimeSmoothL.reset(44100, smoothingTimeInSeconds); 15 | delayTimeSmoothR.reset(44100, smoothingTimeInSeconds); 16 | delayTimeSmoothL.setCurrentAndTargetValue(timeL); 17 | delayTimeSmoothR.setCurrentAndTargetValue(timeR); 18 | } 19 | 20 | void MultiDelay::prepare(const juce::dsp::ProcessSpec& spec) 21 | { 22 | delayLine.prepare(spec); 23 | sampleRate = spec.sampleRate; 24 | 25 | // Update smoothers with correct sample rate 26 | delayTimeSmoothL.reset(sampleRate, smoothingTimeInSeconds); 27 | delayTimeSmoothR.reset(sampleRate, smoothingTimeInSeconds); 28 | delayTimeSmoothL.setCurrentAndTargetValue(timeL); 29 | delayTimeSmoothR.setCurrentAndTargetValue(timeR); 30 | 31 | reset(); 32 | } 33 | 34 | void MultiDelay::reset() 35 | { 36 | delayLine.reset(); 37 | 38 | // Reset smoothers to current delay times 39 | delayTimeSmoothL.setCurrentAndTargetValue(timeL); 40 | delayTimeSmoothR.setCurrentAndTargetValue(timeR); 41 | } 42 | 43 | void MultiDelay::setPingPong(bool enabled) 44 | { 45 | pingPongEnabled = enabled; 46 | } 47 | 48 | void MultiDelay::setFeedback(int channel, float newFeedback) 49 | { 50 | jassert(newFeedback >= 0.0f && newFeedback <= 1.0f); 51 | jassert(channel == 0 || channel == 1); 52 | if (channel == 0) 53 | { 54 | feedbackL = newFeedback; 55 | } 56 | else 57 | { 58 | feedbackR = newFeedback; 59 | } 60 | } 61 | 62 | void MultiDelay::setDelayTime(int channel, float time) 63 | { 64 | jassert(channel == 0 || channel == 1); 65 | if (channel == 0) 66 | { 67 | // Set the target value for smooth transitions 68 | timeL = time; 69 | delayTimeSmoothL.setTargetValue(time); 70 | } 71 | else 72 | { 73 | // Set the target value for smooth transitions 74 | timeR = time; 75 | delayTimeSmoothR.setTargetValue(time); 76 | } 77 | } 78 | 79 | void MultiDelay::setMix(int channel, float newMix) 80 | { 81 | jassert(newMix >= 0.0f && newMix <= 1.0f); 82 | jassert(channel == 0 || channel == 1); 83 | if (channel == 0) 84 | { 85 | mixL = newMix; 86 | } 87 | else 88 | { 89 | mixR = newMix; 90 | } 91 | } 92 | 93 | void MultiDelay::setDelayTimeSmoothingTime(float newSmoothingTimeInSeconds) 94 | { 95 | smoothingTimeInSeconds = newSmoothingTimeInSeconds; 96 | 97 | // Update the smoothing time for both channels 98 | delayTimeSmoothL.reset(sampleRate, smoothingTimeInSeconds); 99 | delayTimeSmoothR.reset(sampleRate, smoothingTimeInSeconds); 100 | } 101 | -------------------------------------------------------------------------------- /plugin/Source/dsp/Sampler.cpp: -------------------------------------------------------------------------------- 1 | #include "Sampler.h" 2 | 3 | Sampler::Sampler() : sampleRate(44100.0) 4 | { 5 | formatManager.registerBasicFormats(); 6 | path = ""; 7 | } 8 | 9 | Sampler::~Sampler() 10 | { 11 | } 12 | 13 | void Sampler::prepare(const juce::dsp::ProcessSpec& spec) 14 | { 15 | sampleRate = spec.sampleRate; 16 | } 17 | 18 | bool Sampler::loadFile(const juce::File& file) 19 | { 20 | auto* reader = formatManager.createReaderFor(file); 21 | 22 | if (reader != nullptr) 23 | { 24 | std::unique_ptr readerPtr(reader); 25 | 26 | sampleBuffer.setSize(reader->numChannels, reader->lengthInSamples); 27 | reader->read(&sampleBuffer, 28 | 0, 29 | reader->lengthInSamples, 30 | 0, 31 | true, // read left channel 32 | true); // read right channel 33 | 34 | fileSampleRate = reader->sampleRate; 35 | path = file.getFullPathName(); 36 | sampleName = file.getFileName(); 37 | DBG("Sampler: loaded sample file: " + file.getFileName()); 38 | DBG("Sampler: length in samples : " + juce::String(reader->lengthInSamples)); 39 | loaded = true; 40 | return true; 41 | } 42 | DBG("Sampler: failed to load sample file: " + file.getFileName()); 43 | loaded = false; 44 | return false; 45 | } 46 | 47 | void Sampler::clear() 48 | { 49 | sampleBuffer.clear(); 50 | fileSampleRate = 0.0; 51 | path = ""; 52 | loaded = false; 53 | } 54 | 55 | float Sampler::getSample(int channel, int position) const 56 | { 57 | if (position >= 0 && position < sampleBuffer.getNumSamples()) 58 | return sampleBuffer.getSample(channel, position); 59 | return 0.0f; 60 | } 61 | 62 | juce::dsp::AudioBlock Sampler::getSubBlock(int start, int length) 63 | { 64 | jassert(start >= 0 && length > 0); 65 | jassert(start + length <= sampleBuffer.getNumSamples()); 66 | 67 | float* const* channelArray = sampleBuffer.getArrayOfWritePointers(); 68 | return juce::dsp::AudioBlock(channelArray, 69 | sampleBuffer.getNumChannels(), 70 | start, // sample offset 71 | length); // number of samples 72 | } 73 | 74 | juce::AudioFormatManager& Sampler::getFormatManager() 75 | { 76 | return formatManager; 77 | } 78 | 79 | int Sampler::getNumSamples() const 80 | { 81 | return sampleBuffer.getNumSamples(); 82 | } 83 | 84 | int Sampler::getNumChannels() const 85 | { 86 | return sampleBuffer.getNumChannels(); 87 | } 88 | 89 | double Sampler::getSampleRate() const 90 | { 91 | return sampleRate; 92 | } 93 | 94 | bool Sampler::isLoaded() const 95 | { 96 | return loaded; 97 | } 98 | 99 | double Sampler::getFileSampleRate() const 100 | { 101 | return fileSampleRate; 102 | } 103 | 104 | juce::String& Sampler::getFilePath() 105 | { 106 | return path; 107 | } 108 | 109 | juce::String& Sampler::getSampleName() 110 | { 111 | return sampleName; 112 | } 113 | -------------------------------------------------------------------------------- /plugin/Source/PluginProcessor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ResonatorSynth.h" 4 | #include "Parameters.h" 5 | #include 6 | #include "GlobalState.h" 7 | #include 8 | #include 9 | 10 | //============================================================================== 11 | class ResonariumProcessor : public gin::Processor 12 | { 13 | public: 14 | ResonariumProcessor(); 15 | ~ResonariumProcessor() override; 16 | 17 | void stateUpdated() override; 18 | void updateState() override; 19 | void reset() override; 20 | void prepareToPlay(double sampleRate, int samplesPerBlock) override; 21 | void releaseResources() override; 22 | void processBlock(juce::AudioBuffer&, juce::MidiBuffer&) override; 23 | 24 | juce::AudioProcessorEditor* createEditor() override; 25 | bool hasEditor() const override; 26 | bool supportsMPE() const override { return true; } 27 | void setupModMatrix(); 28 | static gin::ProcessorOptions getOptions(); 29 | 30 | // Get the synthesizer instance (needed for Python bindings) 31 | ResonatorSynth* getSynth() { return &synth; } 32 | 33 | ResonatorSynth synth; 34 | UIParams uiParams; 35 | GlobalState globalState; 36 | gin::AudioFifo scopeFifo { 2, 44100 }; 37 | int pluginInstanceID = -1; 38 | bool prepared = false; //indicates that prepareToPlay has been called at least once 39 | bool soloActive = false; //true if a resonator is in solo mode 40 | 41 | bool simulateConstantNote; 42 | bool constantNoteActive; 43 | const int constantNoteNumber = 48 + 5; 44 | const float constantNoteVelocity = 0.5f; 45 | const int constantNoteChannel = 15; 46 | float constantNoteFrequency = 440.0f; 47 | 48 | #if PERFETTO 49 | std::unique_ptr tracingSession; 50 | #endif 51 | 52 | static bool checkBufferForNaN(juce::dsp::AudioBlock block, juce::String caller = "") 53 | { 54 | for (size_t channelIdx = 0; channelIdx < block.getNumChannels(); channelIdx++) 55 | { 56 | for (size_t sampleIdx = 0; sampleIdx < block.getNumSamples(); sampleIdx++) 57 | { 58 | if (std::isnan(block.getSample(channelIdx, sampleIdx))) 59 | { 60 | DBG(caller + ": NaN detected in channel " + juce::String(channelIdx) + " at sample " + juce::String(sampleIdx)); 61 | juce::Logger::writeToLog(caller + ": NaN detected in channel " + juce::String(channelIdx) + " at sample " + juce::String(sampleIdx)); 62 | juce::String blockString = ""; 63 | for (size_t k = 0; k < block.getNumSamples(); k++) 64 | { 65 | blockString += juce::String(block.getSample(channelIdx, k)) + " "; 66 | } 67 | DBG(blockString); 68 | juce::Logger::writeToLog(blockString); 69 | return true; 70 | } 71 | } 72 | } 73 | 74 | return false; 75 | } 76 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ResonariumProcessor) 77 | }; 78 | -------------------------------------------------------------------------------- /plugin/Source/ResonatorVoice.h: -------------------------------------------------------------------------------- 1 | #ifndef RESONATORVOICE_H 2 | #define RESONATORVOICE_H 3 | 4 | #pragma once 5 | 6 | #include "Exciters.h" 7 | #include "WaveguideResonatorBank.h" 8 | #include "dsp/ResonariumEffectChain.h" 9 | #include "util/RandomLFO.h" 10 | #include "util/StereoLFOWrapper.h" 11 | #include "util/StereoMSEGWrapper.h" 12 | #include "util/WrappedEnvelope.h" 13 | 14 | class ResonariumProcessor; 15 | 16 | 17 | class ResonatorVoice : public gin::SynthesiserVoice, public gin::ModVoice 18 | { 19 | public: 20 | ResonatorVoice(GlobalState& state, VoiceParams params); 21 | ~ResonatorVoice() override; 22 | void noteStarted() override; 23 | void noteStopped(bool allowTailOff) override; 24 | void noteRetriggered() override; 25 | float getCurrentNote() override; 26 | void notePressureChanged() override; 27 | void noteTimbreChanged() override; 28 | void notePitchbendChanged() override; 29 | void noteKeyStateChanged() override; 30 | void prepare(const juce::dsp::ProcessSpec& spec); 31 | void updateParameters(int numSamples); 32 | void renderNextBlock(juce::AudioBuffer& outputBuffer, int startSample, int numSamples) override; 33 | bool isVoiceActive() override; 34 | 35 | GlobalState& state; 36 | VoiceParams params; 37 | float frequency = 440.0f; 38 | gin::EasedValueSmoother noteSmoother; 39 | float currentMidiNote = 64; 40 | int id = 0; 41 | juce::OwnedArray resonatorBanks; 42 | float gain = 1.0f; 43 | 44 | int currentBlockStartSample = 0; //start sample of the current block 45 | int currentBlockNumSamples = 64; //num samples in the current block 46 | 47 | bool noteReleased = false; 48 | int silenceCount = 0; 49 | int silenceCountThreshold = 2; //how many quiet samples before we stop the voice? 50 | int numBlocksSinceNoteOn = 0; // what it says on the tin. 51 | 52 | int testForSilenceBlockPeriod = 15; 53 | int testForSilenceBlockCount = 0; 54 | 55 | juce::AudioBuffer exciterBuffer; // buffer for exciters to write to, is routed to resonator banks 56 | juce::AudioBuffer resonatorBankBuffer; // buffer for resonator banks to write to, is routed to output 57 | juce::AudioBuffer tempBuffer; // temporary buffer for processing 58 | juce::AudioBuffer soloBuffer; // if a resonator's solo button is toggled, that resonator's output is routed here 59 | 60 | juce::dsp::ProcessorDuplicator, juce::dsp::IIR::Coefficients> dcBlockers[NUM_RESONATORS]; 61 | 62 | juce::OwnedArray exciters; 63 | ExternalInputExciter* extInExciter; //alias for the external input exciter which requires some special treatment 64 | 65 | std::array polyLFOs; 66 | std::array polyRandomLFOs; 67 | std::array polyEnvelopes; 68 | juce::Array polyMSEGs; //have to use heap array since mseg has no default constructor, 69 | juce::NormalisableRange skewedFrequencyRange; //required to appropriately skew the frequency modulation src 70 | 71 | ResonariumEffectChain effectChain; 72 | 73 | bool bypassResonators = false; 74 | }; 75 | 76 | 77 | #endif //RESONATORVOICE_H 78 | -------------------------------------------------------------------------------- /python-examples/simple-demo.py: -------------------------------------------------------------------------------- 1 | import resonarium 2 | import numpy as np 3 | from scipy.io import wavfile 4 | 5 | def write_to_wav(buffer, filename): 6 | audio_data = buffer.T 7 | 8 | # Normalize the audio to prevent clipping 9 | max_val = np.max(np.abs(audio_data)) 10 | if max_val > 0: 11 | audio_data = audio_data / max_val 12 | 13 | # Convert to 16-bit PCM format 14 | audio_data_16bit = (audio_data * 32767).astype(np.int16) 15 | 16 | # Save as WAV file 17 | wavfile.write(filename, int(sample_rate), audio_data_16bit) 18 | 19 | # Initialize the synth with default settings (44.1kHz, 512 block size) 20 | synth = resonarium.Resonarium() 21 | 22 | # Let's see what parameters we can play with... 23 | # for param in synth.get_all_params(): 24 | # print(f"{param.id}: {param.value}") 25 | # print(f" Range: {param.min} to {param.max}") 26 | 27 | # Everything is disabled by default. Let's enable the impulse exciter and a single string model, with a bright loop filter 28 | # "wb0r0" corresponds to the first string model in the first waveguide bank -- indexing starts at zero. There are four waveguide banks, with eight string models apiece. 29 | synth.set_param("impExciter0 enabled", 1.0) 30 | synth.set_param("enabled wb0r0", 1.0) 31 | synth.set_param("decayFilterCutoff wb0r0", 7000) 32 | 33 | # Create an audio buffer that stores a few seconds of audio 34 | sample_rate = synth.get_sample_rate() 35 | block_size = synth.get_block_size() 36 | seconds = 2.0 37 | 38 | # Calculate how many blocks we need for the number of seconds 39 | blocks_needed = int(np.ceil((sample_rate * seconds) / block_size)) 40 | 41 | # Allocate the corresponding memory 42 | audio_buffer = synth.create_multi_block(blocks_needed) 43 | 44 | # Play a MIDI note (middle C = 60) with velocity 100 45 | synth.play_note(0, 60, 100) 46 | 47 | # Process audio. This will fill the buffer with audio, and stop when the buffer is full. 48 | synth.process_multi_block(audio_buffer) 49 | 50 | # We might want to add MIDI events or change parameters during processing. 51 | # In that case, we can call process_multi_block multiple times and specify the start and end blocks. 52 | # We'll have to increment the current block index on the Python side, since the buffer doesn't "remember" the current write position. 53 | # e.g.: synth.process_multi_block(audio_buffer, start_block, end_block) 54 | 55 | write_to_wav(audio_buffer, "processor_example.wav") 56 | 57 | # In this example we directly manipulate and read audio from a single voice 58 | audio_buffer.fill(0.0) 59 | synth = resonarium.Resonarium() 60 | synth.set_param("impExciter0 enabled", 1.0) 61 | synth.set_param("enabled wb0r0", 1.0) 62 | synth.set_param("decayFilterCutoff wb0r0", 7000) 63 | 64 | # Get the 0th voice in the voices array. The voices array, in theory, should not be modified after instantiation... 65 | # therefore, this should always return a pointer to the same voice object in memory (if you always use the same index) 66 | voice = synth.get_voice(0) 67 | 68 | # Make the voice play a note 69 | voice.play_note(60, 100) # notice 70 | voice.process_multi_block(audio_buffer) 71 | voice.release_note() 72 | voice.reset() 73 | 74 | # You can now have the voice generate more data into a new buffer by repeating the above four lines 75 | # just make sure to start, stop, and reset the voice, and make sure the buffer is cleared each time 76 | 77 | write_to_wav(audio_buffer, "voice_example.wav") 78 | print("Done! :)") 79 | -------------------------------------------------------------------------------- /plugin/Source/dsp/MultiAmp.h: -------------------------------------------------------------------------------- 1 | #ifndef MULTIAMP_H 2 | #define MULTIAMP_H 3 | 4 | #include "airwindows/BassAmp.h" 5 | #include "airwindows/BigAmp.h" 6 | #include "airwindows/DeRez2.h" 7 | #include "airwindows/GrindAmp.h" 8 | #include "airwindows/FireAmp.h" 9 | #include "airwindows/LeadAmp.h" 10 | #include "../Parameters.h" 11 | 12 | struct MultiAmpParams; 13 | /** 14 | * A distortion multi-effect that wraps several amp simulation algorithms, 15 | * as well as several Airwindows amp simulators. 16 | */ 17 | class MultiAmp 18 | { 19 | public: 20 | enum Mode 21 | { 22 | LEAD, 23 | FIRE, 24 | GRIND, 25 | BIG, 26 | DEREZ, 27 | BASS 28 | }; 29 | 30 | explicit MultiAmp(std::function sampleRateCallback, MultiAmpParams params); 31 | 32 | void reset(); 33 | 34 | template 35 | void updateParameters(T& source) 36 | { 37 | mode = static_cast(params.mode->getProcValue()); 38 | switch (mode) 39 | { 40 | case BASS: 41 | bassAmp.setParameter(0, source.getValue(params.paramA)); 42 | bassAmp.setParameter(1, source.getValue(params.paramB)); 43 | bassAmp.setParameter(2, source.getValue(params.paramC)); 44 | bassAmp.setParameter(3, source.getValue(params.paramD)); 45 | break; 46 | case BIG: 47 | bigAmp.setParameter(0, source.getValue(params.paramA)); 48 | bigAmp.setParameter(1, source.getValue(params.paramB)); 49 | bigAmp.setParameter(2, source.getValue(params.paramC)); 50 | bigAmp.setParameter(3, source.getValue(params.paramD)); 51 | break; 52 | case DEREZ: 53 | deRez2.setParameter(0, source.getValue(params.paramA)); 54 | deRez2.setParameter(1, source.getValue(params.paramB)); 55 | deRez2.setParameter(2, source.getValue(params.paramC)); 56 | deRez2.setParameter(3, source.getValue(params.paramD)); 57 | break; 58 | case FIRE: 59 | fireAmp.setParameter(0, source.getValue(params.paramA)); 60 | fireAmp.setParameter(1, source.getValue(params.paramB)); 61 | fireAmp.setParameter(2, source.getValue(params.paramC)); 62 | fireAmp.setParameter(3, source.getValue(params.paramD)); 63 | break; 64 | case GRIND: 65 | grindAmp.setParameter(0, source.getValue(params.paramA)); 66 | grindAmp.setParameter(1, source.getValue(params.paramB)); 67 | grindAmp.setParameter(2, source.getValue(params.paramC)); 68 | grindAmp.setParameter(3, source.getValue(params.paramD)); 69 | break; 70 | case LEAD: 71 | leadAmp.setParameter(0, source.getValue(params.paramA)); 72 | leadAmp.setParameter(1, source.getValue(params.paramB)); 73 | leadAmp.setParameter(2, source.getValue(params.paramC)); 74 | leadAmp.setParameter(3, source.getValue(params.paramD)); 75 | break; 76 | default: 77 | jassertfalse; 78 | } 79 | } 80 | 81 | static juce::String getParameterName(Mode mode, int index); 82 | 83 | void process(juce::dsp::ProcessContextReplacing context); 84 | 85 | MultiAmpParams params; 86 | 87 | BassAmp bassAmp; 88 | BigAmp bigAmp; 89 | DeRez2 deRez2; 90 | FireAmp fireAmp; 91 | GrindAmp grindAmp; 92 | LeadAmp leadAmp; 93 | 94 | Mode mode; 95 | }; 96 | 97 | 98 | #endif //MULTIAMP_H 99 | -------------------------------------------------------------------------------- /plugin/Source/util/InterpolatedParameter.h: -------------------------------------------------------------------------------- 1 | #ifndef INTERPOLATEDPARAMETER_H 2 | #define INTERPOLATEDPARAMETER_H 3 | 4 | /** 5 | * Smoothly interpolates between two values of a gin::Parameter over a specified number of samples. 6 | * For use within a single block of processing. 7 | */ 8 | class InterpolatedParameter 9 | { 10 | public: 11 | 12 | explicit InterpolatedParameter(gin::Parameter::Ptr parameter) : parameter(parameter) 13 | { 14 | current[0] = parameter->getProcValue(); 15 | current[1] = parameter->getProcValue(); 16 | target[0] = current[0]; 17 | target[1] = current[1]; 18 | filterState[0] = current[0]; 19 | filterState[1] = current[1]; 20 | samplesRemaining = -1; 21 | increment[0] = 0; 22 | increment[1] = 0; 23 | } 24 | 25 | /** 26 | * Sets the target value of the InterpolatedParameter to the specified value, 27 | * using modulation from the specifed source (either the monophonic ModMatrix or a polyphonic ModVoice). 28 | */ 29 | template 30 | void setTargetValue(T source, int numSamples) 31 | { 32 | jassert(numSamples >= 0); 33 | if(numSamples == 0) 34 | { 35 | numSamples = 1; 36 | } 37 | target[0] = source.getValue(parameter, 0); 38 | target[1] = source.getValue(parameter, 1); 39 | if(current[0] == target[0]) 40 | { 41 | noop[0] = true; 42 | } 43 | if(current[1] == target[1]) 44 | { 45 | noop[1] = true; 46 | } 47 | increment[0] = (target[0] - current[0]) / static_cast(numSamples); 48 | increment[1] = (target[1] - current[1]) / static_cast(numSamples); 49 | samplesRemaining = numSamples; 50 | } 51 | 52 | /** 53 | * Snaps both the current and target values of the InterpolatedParameter to the specified source value, 54 | * and resets the filter state. 55 | */ 56 | template 57 | void snapValue(T source) 58 | { 59 | current[0] = source.getValue(parameter, 0); 60 | current[1] = source.getValue(parameter, 1); 61 | target[0] = current[0]; 62 | target[1] = current[1]; 63 | filterState[0] = current[0]; 64 | filterState[1] = current[1]; 65 | } 66 | 67 | /** 68 | * Performs a single step of the interpolation process, returning the next value of the InterpolatedParameter. 69 | * This function should only be called numSamples times before calling setTargetValue() again. 70 | */ 71 | float nextValue(int channel = 0) 72 | { 73 | jassert(channel == 0 || channel == 1); 74 | jassert(samplesRemaining > 0); 75 | samplesRemaining--; 76 | current[channel] += increment[channel]; 77 | if(samplesRemaining == 0) 78 | { 79 | current[channel] = target[channel]; 80 | } 81 | return processOnePole(current[channel], channel); 82 | } 83 | 84 | private: 85 | 86 | inline float processOnePole(float input, int channel = 0) 87 | { 88 | filterState[channel] = input + onePoleCoefficient * (filterState[channel] - input); 89 | return filterState[channel]; 90 | } 91 | 92 | gin::Parameter::Ptr parameter; 93 | int samplesRemaining; 94 | float filterState[2]; 95 | float current[2]; 96 | float target[2]; 97 | float increment[2]; 98 | bool noop[2]; 99 | 100 | float onePoleCoefficient = 44100.0f - 44.0f / 44100.0f; 101 | }; 102 | 103 | 104 | #endif //INTERPOLATEDPARAMETER_H 105 | -------------------------------------------------------------------------------- /python-bindings.md: -------------------------------------------------------------------------------- 1 | # Python Bindings 2 | 3 | Resonarium supports basic Python bindings. They are a work in progress. At the moment, the bindings support getting/setting synth parameters, playing MIDI notes, modulating parameters, and recording audio to memory. 4 | 5 | To compile the Python library, build the `Resonarium_Python` CMake target, e.g.: 6 | 7 | ```bash 8 | cmake --build build --target Resonarium_Python 9 | ``` 10 | 11 | You'll find the `.so` library file in the build directory. By default, the library is not installed system-wide. Only MacOS is supported as of writing. 12 | 13 | The bindings can be found in `ResonariumPy.cpp`. An LLM should be able to generate documentation given the `.cpp` file and the appropriate prompt. The simple script below demonstrates how to manipulate parameters, play MIDI, and record the result. 14 | 15 | ```python 16 | 17 | import resonarium 18 | import numpy as np 19 | from scipy.io import wavfile 20 | 21 | # Initialize the synth with default settings (44.1kHz, 512 block size) 22 | synth = resonarium.Resonarium() 23 | 24 | # Let's see what parameters we can play with... 25 | for param in synth.get_all_params(): 26 | print(f"{param.id}: {param.value}") 27 | print(f" Range: {param.min} to {param.max}") 28 | 29 | # Everything is disabled by default. Let's enable the impulse exciter and a single string model, with a bright loop filter 30 | # "wb0r0" corresponds to the first string model in the first waveguide bank -- indexing starts at zero. There are four waveguide banks, with eight string models apiece. 31 | synth.set_param("impExciter0 enabled", 1.0) 32 | synth.set_param("enabled wb0r0", 1.0) 33 | synth.set_param("decayFilterCutoff wb0r0", 7000) 34 | 35 | # Create an audio buffer that stores about 4 seconds of audio 36 | sample_rate = synth.get_sample_rate() 37 | block_size = synth.get_block_size() 38 | seconds = 4.0 39 | 40 | # Calculate how many blocks we need for 4 seconds 41 | blocks_needed = int(np.ceil((sample_rate * seconds) / block_size)) 42 | 43 | # Allocate the corresponding memory 44 | audio_buffer = synth.create_multi_block(blocks_needed) 45 | 46 | # Play a MIDI note (middle C = 60) with velocity 100 47 | synth.play_note(0, 60, 100) 48 | 49 | # Process audio. This will fill the buffer with audio, and stop when the buffer is full. 50 | # We allocated about 4 seconds worth of audio blocks, so we should get a 4 second sample back. 51 | synth.process_multi_block(audio_buffer) 52 | 53 | # We might want to add MIDI events or change parameters during processing. 54 | # In that case, we can call process_multi_block multiple times and specify the start and end blocks. 55 | # We'll have to increment the current block index on the Python side, since the buffer doesn't "remember" the current write position. 56 | # e.g.: synth.process_multi_block(audio_buffer, start_block, end_block) 57 | 58 | # Convert the numpy array to the right format for saving 59 | # The audio_buffer is in (channels, samples) format, we need (samples, channels) 60 | audio_data = audio_buffer.T 61 | 62 | # Normalize the audio to prevent clipping 63 | max_val = np.max(np.abs(audio_data)) 64 | if max_val > 0: 65 | audio_data = audio_data / max_val 66 | 67 | # Convert to 16-bit PCM format 68 | audio_data_16bit = (audio_data * 32767).astype(np.int16) 69 | 70 | # Save as WAV file 71 | wavfile.write('resonarium_output.wav', int(sample_rate), audio_data_16bit) 72 | print("All done! :)") 73 | ``` 74 | 75 | ## Parameter IDs 76 | At the moment, there are no higher-level Python structures (e.g. LFOs, MSEGs, etc) that map directly to their internal C++ counterparts. Instead, Resonarium's internal state is manipulated through direct access to internal and external parameters. The parameter ID naming scheme is somewhat inconsistent; this will be changed eventually. 77 | 78 | All the parameters are defined in `Parameters.cpp`. You can review this file manually to see what parameters are available, or feed it into an LLM and ask questions. 79 | -------------------------------------------------------------------------------- /plugin/Source/dsp/Distortion.cpp: -------------------------------------------------------------------------------- 1 | #include "Distortion.h" 2 | 3 | Distortion::Distortion(DistortionParams params) 4 | : params(params), distortionMode(SOFT_CLIP) 5 | { 6 | } 7 | 8 | void Distortion::prepare(const juce::dsp::ProcessSpec& spec) 9 | { 10 | filter.prepare(spec); 11 | reset(); 12 | } 13 | 14 | void Distortion::reset() 15 | { 16 | lastDownSampleValue = 0.0f; 17 | downSampleCounter = 0.0f; 18 | filter.reset(); 19 | } 20 | 21 | void Distortion::process(juce::dsp::ProcessContextReplacing context) 22 | { 23 | auto& inputBlock = context.getInputBlock(); 24 | auto& outputBlock = context.getOutputBlock(); 25 | auto numSamples = inputBlock.getNumSamples(); 26 | auto numChannels = inputBlock.getNumChannels(); 27 | 28 | jassert(inputBlock.getNumChannels() == 2); 29 | jassert(outputBlock.getNumChannels() == 2); 30 | 31 | if (filterMode == pre) 32 | { 33 | filter.process(context); 34 | } 35 | 36 | for (size_t channel = 0; channel < numChannels; ++channel) 37 | { 38 | auto* input = inputBlock.getChannelPointer(channel); 39 | auto* output = outputBlock.getChannelPointer(channel); 40 | float scaledDrive = std::pow(10.0f, drive[channel] / 20.0f); 41 | float scaledOutput = std::pow(10.0f, outputGain[channel] / 20.0f); 42 | for (size_t sample = 0; sample < numSamples; ++sample) 43 | { 44 | float inSample = input[sample]; 45 | float outSample = inSample; 46 | 47 | switch (distortionMode) 48 | { 49 | case SOFT_CLIP: 50 | { 51 | float scaledValue = inSample * scaledDrive; 52 | outSample = std::tanh(scaledValue) * scaledOutput; 53 | break; 54 | } 55 | case HARD_CLIP: 56 | { 57 | float scaledValue = inSample * scaledDrive; 58 | outSample = juce::jlimit(-1.0f, 1.0f, scaledValue) * scaledOutput; 59 | break; 60 | } 61 | case LINEAR_FOLD: 62 | { 63 | float adjust = inSample * scaledDrive * 0.25f + 0.75f; 64 | float range = std::fmod(adjust, 1.0f); 65 | outSample = (std::abs(range * -4.0f + 2.0f) - 1.0f) * scaledOutput; 66 | break; 67 | } 68 | case SIN_FOLD: 69 | { 70 | float adjust = inSample * scaledDrive * -0.25f + 0.5f; 71 | float range = std::fmod(adjust, 1.0f); 72 | outSample = std::sin(range * 2.0f * juce::MathConstants::pi) * scaledOutput; 73 | break; 74 | } 75 | case BIT_CRUSH: 76 | { 77 | int levels = juce::jlimit(1, 32, static_cast(drive[channel])); 78 | float step = 2.0f / levels; // Assuming input signal is in [-1, 1] 79 | outSample = std::round(inSample / step) * step * scaledOutput; 80 | break; 81 | } 82 | case DOWN_SAMPLE: 83 | { 84 | float period = juce::jmax(1.0f, drive[channel]); 85 | downSampleCounter += 1.0f; 86 | if (downSampleCounter >= period) 87 | { 88 | downSampleCounter -= period; 89 | lastDownSampleValue = inSample; 90 | } 91 | outSample = lastDownSampleValue * scaledOutput; 92 | break; 93 | } 94 | default: 95 | { 96 | jassertfalse; 97 | break; 98 | } 99 | } 100 | 101 | output[sample] = outSample; 102 | } 103 | } 104 | 105 | if (filterMode == post) 106 | { 107 | filter.process(context); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /plugin/Source/PluginEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "PluginProcessor.h" 4 | #include "melatonin_inspector/melatonin_inspector.h" 5 | #include "ui/AnimatedScrollBarsViewport.h" 6 | #include "ui/ResonariumComponents.h" 7 | #include "ui/Panels.h" 8 | #include "ui/SettingsPanel.h" 9 | 10 | class ResonariumEditor : public gin::ProcessorEditor, 11 | public juce::DragAndDropContainer 12 | { 13 | public: 14 | ResonariumEditor(ResonariumProcessor& p); 15 | ~ResonariumEditor() override; 16 | 17 | //============================================================================== 18 | void paint(juce::Graphics& g) override; 19 | void resized() override; 20 | void showOnboardingWarning(); 21 | void showSettingsMenu(); // Method to show settings menu 22 | 23 | // Handle mouse clicks for logo and text 24 | void mouseDown(const juce::MouseEvent& event) override 25 | { 26 | // Check if the click is on the logo or text 27 | if (event.eventComponent == logo.getComponent() || 28 | event.eventComponent == logoText.getComponent()) 29 | { 30 | showSettingsMenu(); 31 | } 32 | } 33 | 34 | ResonariumProcessor& proc; 35 | UIParams uiParams; 36 | 37 | // juce::TooltipWindow tooltipWindow; 38 | juce::ScopedMessageBox loudnessWarningBox; 39 | SafePointer loudnessWarningBox_v2; 40 | 41 | SafePointer logoImg; //NYI TODO implement gradient logo, fow now, just text 42 | SafePointer logoText; 43 | SafePointer logo; 44 | SafePointer versionText; 45 | SafePointer scope; 46 | 47 | // No need to store a reference to the settings panel 48 | // since we'll create it on demand in showSettingsMenu() 49 | 50 | std::vector> resonatorBankParamBoxes; 51 | 52 | std::vector> impulseExciterParamBoxes; 53 | std::vector> noiseExciterParamBoxes; 54 | std::vector> impulseTrainExciterParamBoxes; 55 | 56 | 57 | SafePointer excitersViewport; 58 | SafePointer excitersViewportContentComponent; 59 | SafePointer impulseExciterParamBox; 60 | SafePointer noiseExciterParamBox; 61 | SafePointer impulseTrainExciterParamBox; 62 | SafePointer sampleExciterParamBox; 63 | SafePointer extInParamBox; 64 | 65 | std::vector> lfoParamBoxes; 66 | std::vector> randomLfoParamBoxes; 67 | std::vector> msegParamBoxes; 68 | std::vector> envelopeParamBoxes; 69 | SafePointer macroParamBox; 70 | SafePointer matrixParamBox; 71 | SafePointer modSourceParamBox; 72 | 73 | SafePointer effectsViewport; 74 | SafePointer effectsViewportContentComponent; 75 | SafePointer chorusParamBox; 76 | SafePointer phaserParamBox; 77 | SafePointer delayParamBox; 78 | SafePointer distortionParamBox; 79 | SafePointer multiAmpParamBox; 80 | SafePointer compressorParamBox; 81 | SafePointer reverbParamBox; 82 | SafePointer filter1ParamBox; 83 | SafePointer filter2ParamBox; 84 | SafePointer globalParamBox; 85 | 86 | std::unique_ptr inspector; 87 | gin::SynthesiserUsage usage { proc.synth }; 88 | gin::ModulationOverview modOverview { proc.globalState.modMatrix }; 89 | 90 | //debug buttons, invisible on a release build 91 | juce::TextButton inspectButton{"INSPECT UI"}; 92 | juce::TextButton bypassResonatorsButton{"BYPASS RESONATORS"}; 93 | juce::TextButton captureButton{"CAPTURE"}; 94 | juce::TextButton settingsButton{"SETTINGS"}; 95 | SettingsPanel* settingsPanel; 96 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ResonariumEditor) 97 | }; 98 | -------------------------------------------------------------------------------- /plugin/Resources/Presets/Wooden Bells.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 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 | -------------------------------------------------------------------------------- /plugin/Source/dsp/airwindows/BigAmp.h: -------------------------------------------------------------------------------- 1 | /* ======================================== 2 | * BigAmp - BigAmp.h 3 | * Created 8/12/11 by SPIAdmin 4 | * Copyright (c) 2011 __MyCompanyName__, Airwindows uses the MIT license 5 | * ======================================== */ 6 | 7 | #pragma once 8 | 9 | #include "FXBase.h" 10 | #include 11 | #include 12 | #include 13 | 14 | class BigAmp final : 15 | public AudioEffectX 16 | { 17 | public: 18 | enum { 19 | kParamA = 0, 20 | kParamB = 1, 21 | kParamC = 2, 22 | kParamD = 3, 23 | kNumParameters = 4 24 | }; // 25 | 26 | static constexpr int kNumPrograms = 0; 27 | static constexpr int kNumInputs = 2; 28 | static constexpr int kNumOutputs = 2; 29 | static constexpr unsigned long kUniqueId = 'biga'; //Change this to what the AU identity is! 30 | 31 | BigAmp(audioMasterCallback audioMaster); 32 | ~BigAmp(); 33 | void reset(); 34 | virtual bool getEffectName(char* name); // The plug-in name 35 | virtual VstPlugCategory getPlugCategory(); // The general category for the plug-in 36 | virtual bool getProductString(char* text); // This is a unique plug-in string provided by Steinberg 37 | virtual bool getVendorString(char* text); // Vendor info 38 | virtual VstInt32 getVendorVersion(); // Version number 39 | virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames); 40 | virtual void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames); 41 | virtual void getProgramName(char *name); // read the name from the host 42 | virtual void setProgramName(char *name); // changes the name of the preset displayed in the host 43 | virtual VstInt32 getChunk (void** data, bool isPreset); 44 | virtual VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset); 45 | virtual float getParameter(VstInt32 index); // get the parameter value at the specified index 46 | virtual void setParameter(VstInt32 index, float value); // set the parameter at index to value 47 | virtual void getParameterLabel(VstInt32 index, char *text); // label for the parameter (eg dB) 48 | static void getParameterName(VstInt32 index, char *text); // name of the parameter 49 | virtual void getParameterDisplay(VstInt32 index, char *text); // text description of the current value 50 | virtual VstInt32 canDo(char *text); 51 | private: 52 | char _programName[kVstMaxProgNameLen + 1]; 53 | std::set< std::string > _canDo; 54 | 55 | double lastSampleL; 56 | double storeSampleL; 57 | double lastSlewL; 58 | double iirSampleAL; 59 | double iirSampleBL; 60 | double iirSampleCL; 61 | double iirSampleDL; 62 | double iirSampleEL; 63 | double iirSampleFL; 64 | double iirSampleGL; 65 | double iirSampleHL; 66 | double iirSampleIL; 67 | double iirSampleJL; 68 | double OddL[257]; 69 | double EvenL[257]; //amp 70 | 71 | double lastSampleR; 72 | double storeSampleR; 73 | double lastSlewR; 74 | double iirSampleAR; 75 | double iirSampleBR; 76 | double iirSampleCR; 77 | double iirSampleDR; 78 | double iirSampleER; 79 | double iirSampleFR; 80 | double iirSampleGR; 81 | double iirSampleHR; 82 | double iirSampleIR; 83 | double iirSampleJR; 84 | double OddR[257]; 85 | double EvenR[257]; 86 | 87 | bool flip; 88 | int count; //amp 89 | 90 | double bL[90]; 91 | double lastCabSampleL; 92 | double smoothCabAL; 93 | double smoothCabBL; //cab 94 | 95 | double bR[90]; 96 | double lastCabSampleR; 97 | double smoothCabAR; 98 | double smoothCabBR; //cab 99 | 100 | double lastRefL[10]; 101 | double lastRefR[10]; 102 | int cycle; //undersampling 103 | 104 | enum { 105 | fix_freq, 106 | fix_reso, 107 | fix_a0, 108 | fix_a1, 109 | fix_a2, 110 | fix_b1, 111 | fix_b2, 112 | fix_sL1, 113 | fix_sL2, 114 | fix_sR1, 115 | fix_sR2, 116 | fix_total 117 | }; //fixed frequency biquad filter for ultrasonics, stereo 118 | double fixA[fix_total]; 119 | double fixB[fix_total]; 120 | double fixC[fix_total]; 121 | double fixD[fix_total]; 122 | double fixE[fix_total]; 123 | double fixF[fix_total]; //filtering 124 | 125 | uint32_t fpdL; 126 | uint32_t fpdR; 127 | //default stuff 128 | 129 | float A; 130 | float B; 131 | float C; 132 | float D; 133 | }; -------------------------------------------------------------------------------- /plugin/Source/StereoResonator.h: -------------------------------------------------------------------------------- 1 | #ifndef STEREORESONATOR_H 2 | #define STEREORESONATOR_H 3 | 4 | #include "Parameters.h" 5 | #include "dsp/Filters.h" 6 | #include "util/InterpolatedValue.h" 7 | #include 8 | #include 9 | 10 | class ResonatorVoice; 11 | 12 | class StereoResonator 13 | { 14 | class Resonator 15 | { 16 | public: 17 | 18 | enum class FilterMode 19 | { 20 | LPF = 0, 21 | BPF = 1, 22 | HPF = 2, 23 | }; 24 | 25 | Resonator(ResonatorVoice& voice, ResonatorParams params, int channel) : voice(voice), params(params), 26 | channel(channel), delayLine(21000) 27 | { 28 | jassert(channel == 0 || channel == 1); 29 | } 30 | 31 | /** 32 | * Processes an entire sample through the resonator via computing the output sample after filtering and processing, 33 | * pushing (input + output) into the delay line, and returning just the output sample. 34 | */ 35 | float processSample(float input); 36 | 37 | /** 38 | * Pops a single sample from the resonator's delay line, applies waveguide filtering, and returns it. 39 | * MUST be followed by a call to pushSample. 40 | * This method allows more complex processing of the resonator's feedback input. 41 | */ 42 | float popSample(); 43 | /** 44 | * Push a sample into the delay line. 45 | * An invocation of this function should generally be preceded by an invocation of popSample(). 46 | */ 47 | void pushSample(float input); 48 | 49 | float postProcess(float sample); 50 | 51 | void reset(); 52 | void prepare(const juce::dsp::ProcessSpec& spec); 53 | void updateParameters(float frequency, int numSamples); 54 | void copyParameters(StereoResonator::Resonator& other); 55 | 56 | ResonatorVoice& voice; 57 | ResonatorParams params; 58 | int channel = 0; 59 | float minFrequency = 1.0f; 60 | float maxFrequency = 22000.0f; 61 | float delayLengthInSamples = -1; //the length of the delay line in samples corresponding to frequency 62 | float decayCoefficient; //the first-order damping coefficient 63 | float sampleRate; 64 | 65 | bool passthrough = false; 66 | float passthroughSample; 67 | 68 | float gain; 69 | bool keytrack = true; 70 | float lastFrequency; 71 | float nextFrequency; 72 | float dispersion; 73 | bool loopFilterKeytrack = false; 74 | bool postFilterKeytrack = false; 75 | float postFilterNormalizationScalar = 1; 76 | float loopFilterPhaseDelay = -999999; 77 | 78 | InterpolatedValue delayLengthInterpolator; 79 | 80 | chowdsp::DelayLine delayLine; 81 | chowdsp::SVFMultiMode loopFilter; 82 | chowdsp::SVFMultiMode postFilter; 83 | DispersionFilter apf; 84 | }; 85 | 86 | public: 87 | StereoResonator(ResonatorVoice& voice, ResonatorParams params) 88 | : voice(voice), params(params), 89 | resonators{{voice, params, 0}, {voice, params, 1}}, left(resonators[0]), right(resonators[1]), resonatorIndex(params.resonatorIndex) 90 | { 91 | } 92 | 93 | ResonatorVoice& voice; 94 | ResonatorParams params; 95 | Resonator resonators[2]; 96 | Resonator& left; //handy aliases 97 | Resonator& right; 98 | bool enabled; 99 | int resonatorIndex; 100 | 101 | float processSample(float input, int channel); 102 | 103 | /** 104 | * Pops a single sample from the resonator's delay line, applies waveguide filtering, and returns it. 105 | * MUST be followed by a call to pushSample. 106 | * This method allows more complex processing of the resonator's feedback input. 107 | */ 108 | float popSample(int channel); 109 | /** 110 | * Push a sample into the delay line. 111 | * An invocation of this function should generally be preceeded by an invocation of popSample(). 112 | */ 113 | void pushSample(float input, int channel); 114 | float postProcess(float sample, int channel); 115 | void reset(); 116 | void prepare(const juce::dsp::ProcessSpec& spec); 117 | void updateParameters(float frequency, int numSamples, bool force = false); 118 | private: 119 | static constexpr double INV_SQRT_2 = 0.7071067811865475244008443621048490392848359376884740365883398689; 120 | }; 121 | 122 | 123 | #endif //STEREORESONATOR_H 124 | -------------------------------------------------------------------------------- /plugin/Source/dsp/MultiFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "MultiFilter.h" 2 | 3 | void MultiFilter::prepare(const juce::dsp::ProcessSpec& spec) 4 | { 5 | poly = (voice != nullptr); 6 | this->sampleRate = spec.sampleRate; 7 | 8 | this->freq = 2000.0f; 9 | this->Q = 0.707f; 10 | 11 | coefficients = juce::dsp::IIR::ArrayCoefficients::makeLowPass(sampleRate, freq, Q); 12 | *filterL.coefficients = coefficients; 13 | *filterR.coefficients = coefficients; 14 | 15 | if (params.frequency == nullptr || params.resonance == nullptr || params.type == nullptr) 16 | { 17 | useHostedParams = false; 18 | } 19 | else 20 | { 21 | useHostedParams = true; 22 | } 23 | } 24 | 25 | void MultiFilter::reset() 26 | { 27 | filterL.reset(); 28 | filterR.reset(); 29 | } 30 | 31 | void MultiFilter::setType(Type type) 32 | { 33 | this->type = type; 34 | updateFlag = true; 35 | } 36 | 37 | void MultiFilter::setParameters(float frequency, float q) 38 | { 39 | this->freq = frequency; 40 | this->Q = q; 41 | updateFlag = true; 42 | } 43 | 44 | std::array MultiFilter::makeHighGainBandpass(float sampleRate, float frequency, float Q) 45 | { 46 | jassert(sampleRate > 0.0); 47 | jassert(frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); 48 | jassert(Q > 0.0); 49 | 50 | const auto n = 1 / std::tan(juce::MathConstants::pi * frequency / static_cast(sampleRate)); 51 | const auto nSquared = n * n; 52 | const auto invQ = 1 / Q; 53 | const auto c1 = 1 / (1 + invQ * n + nSquared); 54 | 55 | return { 56 | { 57 | c1 * n, 0, 58 | -c1 * n, 1, 59 | c1 * 2 * (1 - nSquared), 60 | c1 * (1 - invQ * n + nSquared) 61 | } 62 | }; 63 | } 64 | 65 | float MultiFilter::processSample(int channel, float sample) noexcept 66 | { 67 | if (channel == 0) 68 | { 69 | return filterL.processSample(sample); 70 | } 71 | else if (channel == 1) 72 | { 73 | return filterR.processSample(sample); 74 | } 75 | else 76 | { 77 | jassertfalse; 78 | return -1; 79 | } 80 | } 81 | 82 | void MultiFilter::process(juce::dsp::AudioBlock& block) noexcept 83 | { 84 | if(type == none) return; //no-op 85 | size_t numChannels = block.getNumChannels(); 86 | juce::dsp::AudioBlock leftBlock = block.getSingleChannelBlock(0); 87 | juce::dsp::ProcessContextReplacing leftContext(leftBlock); 88 | filterL.process(leftContext); 89 | 90 | if (numChannels > 1) 91 | { 92 | juce::dsp::AudioBlock rightBlock = block.getSingleChannelBlock(1); 93 | juce::dsp::ProcessContextReplacing rightContext(rightBlock); 94 | filterR.process(rightContext); 95 | } 96 | } 97 | 98 | void MultiFilter::updateParameters() 99 | { 100 | if (useHostedParams) 101 | { 102 | this->type = static_cast(static_cast(params.type->getProcValue())); 103 | if (poly) 104 | { 105 | freq = voice->getValue(params.frequency); 106 | Q = voice->getValue(params.resonance); 107 | } 108 | else 109 | { 110 | freq = params.frequency->getProcValue(); 111 | Q = params.resonance->getProcValue(); 112 | } 113 | } 114 | 115 | if (type == none) return; //no-op 116 | 117 | //TODO add optimization via updateFlag to only compute new coefficients when necessary 118 | if (!useHostedParams && !updateFlag) return; // 119 | switch (type) 120 | { 121 | case MultiFilter::Type::lowpass: 122 | coefficients = juce::dsp::IIR::ArrayCoefficients::makeLowPass(sampleRate, freq, Q); 123 | break; 124 | case MultiFilter::Type::highpass: 125 | coefficients = juce::dsp::IIR::ArrayCoefficients::makeHighPass(sampleRate, freq, Q); 126 | break; 127 | case MultiFilter::Type::bandpass: 128 | if (!normalize) 129 | { 130 | coefficients = makeHighGainBandpass(sampleRate, freq, Q); 131 | } 132 | else 133 | { 134 | coefficients = juce::dsp::IIR::ArrayCoefficients::makeBandPass(sampleRate, freq, Q); 135 | } 136 | break; 137 | case MultiFilter::Type::notch: 138 | coefficients = juce::dsp::IIR::ArrayCoefficients::makeNotch(sampleRate, freq, Q); 139 | break; 140 | case MultiFilter::Type::allpass: 141 | coefficients = juce::dsp::IIR::ArrayCoefficients::makeAllPass(sampleRate, freq, Q); 142 | break; 143 | default: 144 | jassertfalse; 145 | break; 146 | } 147 | 148 | *filterL.coefficients = coefficients; 149 | *filterR.coefficients = coefficients; 150 | 151 | updateFlag = false; 152 | } 153 | -------------------------------------------------------------------------------- /plugin/Source/dsp/airwindows/FXBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // Define M_PI for Windows builds 9 | #ifndef M_PI 10 | #define M_PI 3.14159265358979323846 11 | #endif 12 | 13 | #define VstInt32 int32_t 14 | #define AudioEffect FXBase 15 | #define AudioEffectX FXBase 16 | #define audioMasterCallback FXBaseCallback 17 | #define VstPlugCategory int 18 | #define kPlugCategEffect 1 19 | #define kVstMaxProgNameLen 32 20 | #define kVstMaxParamStrLen 32 21 | #define kVstMaxProductStrLen 32 22 | #define kVstMaxVendorStrLen 32 23 | #define vst_strncpy strncpy 24 | 25 | inline void float2string (float f, char* text, int len) 26 | { 27 | int decimals = 0; 28 | if (std::fabs (f) >= 10.0f) 29 | decimals = 1; 30 | else if (std::fabs (f) > 1.0f) 31 | decimals = 2; 32 | else 33 | decimals = 3; 34 | 35 | juce::String str (f, decimals); 36 | str.copyToUTF8 (text, (size_t)len); 37 | } 38 | 39 | inline void int2string (float i, char* text, int len) 40 | { 41 | juce::String str (i); 42 | str.copyToUTF8 (text, (size_t)len); 43 | } 44 | 45 | inline void dB2string (float value, char* text, int maxLen) 46 | { 47 | if (value <= 0) 48 | vst_strncpy (text, "-oo", (size_t) maxLen); 49 | else 50 | float2string (20.0f * log10 (value), text, maxLen); 51 | } 52 | 53 | //============================================================================== 54 | class FXBaseCallback 55 | { 56 | public: 57 | FXBaseCallback (std::function cb_) : cb (cb_) {} 58 | 59 | double getSampleRate() 60 | { 61 | return cb(); 62 | } 63 | 64 | std::function cb; 65 | }; 66 | 67 | //============================================================================== 68 | class FXBase 69 | { 70 | public: 71 | //============================================================================== 72 | FXBase (FXBaseCallback c, int numPrograms_, int numParams_) 73 | : numPrograms (numPrograms_), numParams (numParams_), callback (c) 74 | { 75 | } 76 | 77 | virtual ~FXBase() = default; 78 | 79 | int getNumInputs() { return numInputs; } 80 | int getNumOutputs() { return numOutputs; } 81 | int getNumParameters() { return numParams; } 82 | 83 | //============================================================================== 84 | virtual bool getEffectName(char* name) = 0; 85 | virtual VstPlugCategory getPlugCategory() = 0; 86 | virtual bool getProductString(char* text) = 0; 87 | virtual bool getVendorString(char* text) = 0; 88 | virtual VstInt32 getVendorVersion() = 0; 89 | virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames) = 0; 90 | virtual void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames) = 0; 91 | virtual void getProgramName(char* name) = 0; 92 | virtual void setProgramName(char* name) = 0; 93 | virtual VstInt32 getChunk (void** data, bool isPreset) { juce::ignoreUnused (data, isPreset); return 0; } 94 | virtual VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset) { juce::ignoreUnused (data, byteSize, isPreset); return 0; } 95 | virtual float getParameter(VstInt32 index) { juce::ignoreUnused (index); return 0; } 96 | virtual void setParameter(VstInt32 index, float value) { juce::ignoreUnused (index, value); } 97 | virtual void getParameterLabel(VstInt32 index, char* text) { juce::ignoreUnused (index, text); } 98 | static void getParameterName(VstInt32 index, char* text) { juce::ignoreUnused (index, text); } 99 | virtual void getParameterDisplay(VstInt32 index, char* text) { juce::ignoreUnused (index, text); } 100 | virtual VstInt32 canDo(char *text) = 0; 101 | 102 | protected: 103 | //============================================================================== 104 | void setNumInputs (int numIn) { numInputs = numIn; } 105 | void setNumOutputs (int numOut) { numOutputs = numOut; } 106 | void setUniqueID (int) {} 107 | void canProcessReplacing() {} 108 | void canDoubleReplacing() {} 109 | void programsAreChunks (bool) {} 110 | 111 | int numInputs = 0, numOutputs = 0, numPrograms = 0, numParams = 0; 112 | 113 | FXBaseCallback callback; 114 | 115 | double getSampleRate() { return callback.getSampleRate(); } 116 | }; 117 | -------------------------------------------------------------------------------- /plugin/Source/dsp/MultiDelay.h: -------------------------------------------------------------------------------- 1 | #ifndef MULTIDELAY_H 2 | #define MULTIDELAY_H 3 | 4 | #include 5 | /** 6 | * A delay effect with a continuous blend between stereo and ping-pong modes. 7 | * Supports smooth delay time modulation with high-quality interpolation. 8 | */ 9 | class MultiDelay 10 | { 11 | public: 12 | explicit MultiDelay(float maxDelayInSeconds); 13 | 14 | void prepare(const juce::dsp::ProcessSpec& spec); 15 | void reset(); 16 | 17 | void setPingPong(bool enabled); 18 | void setFeedback(int channel, float feedback); 19 | void setDelayTime(int channel, float time); 20 | void setMix(int channel, float mix); 21 | 22 | // Set smoothing time for delay time changes (in seconds) 23 | void setDelayTimeSmoothingTime(float smoothingTimeInSeconds); 24 | 25 | template 26 | void process(ProcessContext& context) noexcept 27 | { 28 | // For debugging, forceWy ping-pong onOkay 29 | // pingPongEnabled = true; 30 | 31 | auto& inputBlock = context.getInputBlock(); 32 | auto& outputBlock = context.getOutputBlock(); 33 | auto numChannels = inputBlock.getNumChannels(); 34 | auto numSamples = inputBlock.getNumSamples(); 35 | 36 | jassert(numChannels == 2); 37 | 38 | for (size_t i = 0; i < numSamples; ++i) 39 | { 40 | // Update smoothed delay times 41 | const float smoothedTimeL = delayTimeSmoothL.getNextValue(); 42 | const float smoothedTimeR = delayTimeSmoothR.getNextValue(); 43 | 44 | // Get input samples 45 | const float leftIn = inputBlock.getSample(0, i); 46 | const float rightIn = inputBlock.getSample(1, i); 47 | 48 | // Read from delay line - use smooth modulation 49 | const float leftDelay = delayLine.popSample(0, smoothedTimeL * sampleRate, true); 50 | const float rightDelay = delayLine.popSample(1, smoothedTimeR * sampleRate, true); 51 | 52 | float leftDelayInput, rightDelayInput; 53 | 54 | if (pingPongEnabled) 55 | { 56 | // The simple ping-pong implementation: 57 | 58 | // 1. Convert input to mono 59 | const float monoInput = (leftIn + rightIn) * 0.5f; 60 | 61 | // 2. Send mono input to the LEFT channel only 62 | // (We could choose either channel, but let's use left as the starting point) 63 | 64 | // 3. Cross-feedback: swap the channels in the feedback path 65 | const float leftFeedback = rightDelay * feedbackL; // Left gets feedback from RIGHT 66 | const float rightFeedback = leftDelay * feedbackR; // Right gets feedback from LEFT 67 | 68 | // 4. Apply input to left channel only, both channels get cross-feedback 69 | leftDelayInput = monoInput + leftFeedback; // Left gets mono input + right's feedback 70 | rightDelayInput = rightFeedback; // Right only gets feedback from left 71 | } 72 | else 73 | { 74 | // Normal stereo delay (no ping-pong) 75 | const float leftFeedback = leftDelay * feedbackL; 76 | const float rightFeedback = rightDelay * feedbackR; 77 | 78 | leftDelayInput = leftIn + leftFeedback; 79 | rightDelayInput = rightIn + rightFeedback; 80 | } 81 | 82 | // Push new samples to delay line 83 | delayLine.pushSample(0, leftDelayInput); 84 | delayLine.pushSample(1, rightDelayInput); 85 | 86 | // Use the original delayed signals for wet output 87 | const float leftWet = leftDelay; 88 | const float rightWet = rightDelay; 89 | 90 | // Mix dry and wet signals 91 | const float leftOut = leftIn * (1.0f - mixL) + leftWet * mixL; 92 | const float rightOut = rightIn * (1.0f - mixR) + rightWet * mixR; 93 | 94 | // Write output 95 | outputBlock.setSample(0, i, leftOut); 96 | outputBlock.setSample(1, i, rightOut); 97 | } 98 | } 99 | 100 | float sampleRate; 101 | float maxDelayInSeconds; 102 | bool pingPongEnabled; 103 | float feedbackL; 104 | float feedbackR; 105 | float timeL; 106 | float timeR; 107 | float mixL; 108 | float mixR; 109 | 110 | private: 111 | // Use higher quality interpolation (5th order Lagrange) for better results during modulation 112 | chowdsp::DelayLine delayLine; 113 | 114 | // Use juce::SmoothedValue to smooth delay time changes 115 | juce::SmoothedValue delayTimeSmoothL; 116 | juce::SmoothedValue delayTimeSmoothR; 117 | float smoothingTimeInSeconds = 0.05f; // Default 50ms smoothing 118 | }; 119 | 120 | #endif //MULTIDELAY_H -------------------------------------------------------------------------------- /plugin/Source/dsp/airwindows/FireAmp.h: -------------------------------------------------------------------------------- 1 | /* ======================================== 2 | * FireAmp - FireAmp.h 3 | * Created 8/12/11 by SPIAdmin 4 | * Copyright (c) 2011 __MyCompanyName__, Airwindows uses the MIT license 5 | * ======================================== */ 6 | 7 | #pragma once 8 | 9 | #include "FXBase.h" 10 | #include 11 | #include 12 | #include 13 | 14 | class FireAmp final : public AudioEffectX 15 | { 16 | public: 17 | enum { 18 | kParamA = 0, 19 | kParamB = 1, 20 | kParamC = 2, 21 | kParamD = 3, 22 | kNumParameters = 4 23 | }; // 24 | 25 | static constexpr int kNumPrograms = 0; 26 | static constexpr int kNumInputs = 2; 27 | static constexpr int kNumOutputs = 2; 28 | static constexpr unsigned long kUniqueId = 'fira'; //Change this to what the AU identity is! 29 | 30 | FireAmp(audioMasterCallback audioMaster); 31 | ~FireAmp(); 32 | void reset(); 33 | bool getEffectName(char* name); // The plug-in name 34 | VstPlugCategory getPlugCategory(); // The general category for the plug-in 35 | bool getProductString(char* text); // This is a unique plug-in string provided by Steinberg 36 | bool getVendorString(char* text); // Vendor info 37 | VstInt32 getVendorVersion(); // Version number 38 | void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames); 39 | void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames); 40 | void getProgramName(char *name); // read the name from the host 41 | void setProgramName(char *name); // changes the name of the preset displayed in the host 42 | VstInt32 getChunk (void** data, bool isPreset); 43 | VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset); 44 | float getParameter(VstInt32 index); // get the parameter value at the specified index 45 | void setParameter(VstInt32 index, float value); // set the parameter at index to value 46 | void getParameterLabel(VstInt32 index, char *text); // label for the parameter (eg dB) 47 | static void getParameterName(VstInt32 index, char *text); // name of the parameter 48 | void getParameterDisplay(VstInt32 index, char *text); // text description of the current value 49 | VstInt32 canDo(char *text); 50 | private: 51 | char _programName[kVstMaxProgNameLen + 1]; 52 | std::set< std::string > _canDo; 53 | 54 | 55 | double lastSampleL; 56 | double storeSampleL; 57 | double smoothAL; 58 | double smoothBL; 59 | double smoothCL; 60 | double smoothDL; 61 | double smoothEL; 62 | double smoothFL; 63 | double smoothGL; 64 | double smoothHL; 65 | double smoothIL; 66 | double smoothJL; 67 | double smoothKL; 68 | double smoothLL; 69 | double iirSampleAL; 70 | double iirSampleBL; 71 | double iirSampleCL; 72 | double iirSampleDL; 73 | double iirSampleEL; 74 | double iirSampleFL; 75 | double iirSampleGL; 76 | double iirSampleHL; 77 | double iirSampleIL; 78 | double iirSampleJL; 79 | double iirSampleKL; 80 | double iirSampleLL; 81 | double iirLowpassL; 82 | double iirSpkAL; 83 | double iirSpkBL; 84 | double iirSubL; 85 | double OddL[257]; 86 | double EvenL[257]; 87 | 88 | double lastSampleR; 89 | double storeSampleR; 90 | double smoothAR; 91 | double smoothBR; 92 | double smoothCR; 93 | double smoothDR; 94 | double smoothER; 95 | double smoothFR; 96 | double smoothGR; 97 | double smoothHR; 98 | double smoothIR; 99 | double smoothJR; 100 | double smoothKR; 101 | double smoothLR; 102 | double iirSampleAR; 103 | double iirSampleBR; 104 | double iirSampleCR; 105 | double iirSampleDR; 106 | double iirSampleER; 107 | double iirSampleFR; 108 | double iirSampleGR; 109 | double iirSampleHR; 110 | double iirSampleIR; 111 | double iirSampleJR; 112 | double iirSampleKR; 113 | double iirSampleLR; 114 | double iirLowpassR; 115 | double iirSpkAR; 116 | double iirSpkBR; 117 | double iirSubR; 118 | double OddR[257]; 119 | double EvenR[257]; 120 | 121 | bool flip; 122 | int count; //amp 123 | 124 | double bL[90]; 125 | double lastCabSampleL; 126 | double smoothCabAL; 127 | double smoothCabBL; //cab 128 | 129 | double bR[90]; 130 | double lastCabSampleR; 131 | double smoothCabAR; 132 | double smoothCabBR; //cab 133 | 134 | double lastRefL[10]; 135 | double lastRefR[10]; 136 | int cycle; //undersampling 137 | 138 | enum { 139 | fix_freq, 140 | fix_reso, 141 | fix_a0, 142 | fix_a1, 143 | fix_a2, 144 | fix_b1, 145 | fix_b2, 146 | fix_sL1, 147 | fix_sL2, 148 | fix_sR1, 149 | fix_sR2, 150 | fix_total 151 | }; //fixed frequency biquad filter for ultrasonics, stereo 152 | double fixA[fix_total]; 153 | double fixB[fix_total]; 154 | double fixC[fix_total]; 155 | double fixD[fix_total]; 156 | double fixE[fix_total]; 157 | double fixF[fix_total]; //filtering 158 | 159 | uint32_t fpdL; 160 | uint32_t fpdR; 161 | //default stuff 162 | 163 | float A; 164 | float B; 165 | float C; 166 | float D; 167 | }; 168 | -------------------------------------------------------------------------------- /plugin/Source/dsp/airwindows/LeadAmp.h: -------------------------------------------------------------------------------- 1 | /* ======================================== 2 | * LeadAmp - LeadAmp.h 3 | * Created 8/12/11 by SPIAdmin 4 | * Copyright (c) 2011 __MyCompanyName__, Airwindows uses the MIT license 5 | * ======================================== */ 6 | 7 | #ifndef __LeadAmp_H 8 | #define __LeadAmp_H 9 | 10 | #include "FXBase.h" 11 | 12 | #include 13 | #include 14 | #include //Change this to what the AU identity is! 15 | 16 | class LeadAmp final : 17 | public AudioEffectX 18 | { 19 | public: 20 | enum { 21 | kParamA = 0, 22 | kParamB = 1, 23 | kParamC = 2, 24 | kParamD = 3, 25 | kNumParameters = 4 26 | }; // 27 | 28 | static constexpr int kNumPrograms = 0; 29 | static constexpr int kNumInputs = 2; 30 | static constexpr int kNumOutputs = 2; 31 | static constexpr unsigned long kUniqueId = 'leda'; 32 | 33 | LeadAmp(audioMasterCallback audioMaster); 34 | ~LeadAmp(); 35 | void reset(); 36 | virtual bool getEffectName(char* name); // The plug-in name 37 | virtual VstPlugCategory getPlugCategory(); // The general category for the plug-in 38 | virtual bool getProductString(char* text); // This is a unique plug-in string provided by Steinberg 39 | virtual bool getVendorString(char* text); // Vendor info 40 | virtual VstInt32 getVendorVersion(); // Version number 41 | virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames); 42 | virtual void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames); 43 | virtual void getProgramName(char *name); // read the name from the host 44 | virtual void setProgramName(char *name); // changes the name of the preset displayed in the host 45 | virtual VstInt32 getChunk (void** data, bool isPreset); 46 | virtual VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset); 47 | virtual float getParameter(VstInt32 index); // get the parameter value at the specified index 48 | virtual void setParameter(VstInt32 index, float value); // set the parameter at index to value 49 | virtual void getParameterLabel(VstInt32 index, char *text); // label for the parameter (eg dB) 50 | static void getParameterName(VstInt32 index, char *text); // name of the parameter 51 | virtual void getParameterDisplay(VstInt32 index, char *text); // text description of the current value 52 | virtual VstInt32 canDo(char *text); 53 | private: 54 | char _programName[kVstMaxProgNameLen + 1]; 55 | std::set< std::string > _canDo; 56 | 57 | double lastSampleL; 58 | double storeSampleL; 59 | double smoothAL; 60 | double smoothBL; 61 | double smoothCL; 62 | double smoothDL; 63 | double smoothEL; 64 | double smoothFL; 65 | double smoothGL; 66 | double smoothHL; 67 | double smoothIL; 68 | double smoothJL; 69 | double smoothKL; 70 | double iirSampleAL; 71 | double iirSampleBL; 72 | double iirSampleCL; 73 | double iirSampleDL; 74 | double iirSampleEL; 75 | double iirSampleFL; 76 | double iirSampleGL; 77 | double iirSampleHL; 78 | double iirSampleIL; 79 | double iirSampleJL; 80 | double iirSampleKL; 81 | double iirLowpassL; 82 | double iirSpkAL; 83 | double iirSpkBL; 84 | double iirSubL; 85 | double OddL[257]; 86 | double EvenL[257]; 87 | 88 | double lastSampleR; 89 | double storeSampleR; 90 | double smoothAR; 91 | double smoothBR; 92 | double smoothCR; 93 | double smoothDR; 94 | double smoothER; 95 | double smoothFR; 96 | double smoothGR; 97 | double smoothHR; 98 | double smoothIR; 99 | double smoothJR; 100 | double smoothKR; 101 | double iirSampleAR; 102 | double iirSampleBR; 103 | double iirSampleCR; 104 | double iirSampleDR; 105 | double iirSampleER; 106 | double iirSampleFR; 107 | double iirSampleGR; 108 | double iirSampleHR; 109 | double iirSampleIR; 110 | double iirSampleJR; 111 | double iirSampleKR; 112 | double iirLowpassR; 113 | double iirSpkAR; 114 | double iirSpkBR; 115 | double iirSubR; 116 | double OddR[257]; 117 | double EvenR[257]; 118 | 119 | bool flip; 120 | int count; 121 | 122 | double bL[90]; 123 | double lastCabSampleL; 124 | double smoothCabAL; 125 | double smoothCabBL; //cab 126 | 127 | double bR[90]; 128 | double lastCabSampleR; 129 | double smoothCabAR; 130 | double smoothCabBR; //cab 131 | 132 | double lastRefL[10]; 133 | double lastRefR[10]; 134 | int cycle; //undersampling 135 | 136 | enum { 137 | fix_freq, 138 | fix_reso, 139 | fix_a0, 140 | fix_a1, 141 | fix_a2, 142 | fix_b1, 143 | fix_b2, 144 | fix_sL1, 145 | fix_sL2, 146 | fix_sR1, 147 | fix_sR2, 148 | fix_total 149 | }; //fixed frequency biquad filter for ultrasonics, stereo 150 | double fixA[fix_total]; 151 | double fixB[fix_total]; 152 | double fixC[fix_total]; 153 | double fixD[fix_total]; 154 | double fixE[fix_total]; 155 | double fixF[fix_total]; //filtering 156 | 157 | uint32_t fpdL; 158 | uint32_t fpdR; 159 | //default stuff 160 | 161 | float A; 162 | float B; 163 | float C; 164 | float D; 165 | float E; //parameters. Always 0-1, and we scale/alter them elsewhere. 166 | 167 | }; 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /plugin/Source/ui/SettingsPanel.cpp: -------------------------------------------------------------------------------- 1 | #include "SettingsPanel.h" 2 | 3 | SettingsPanel::SettingsPanel(ResonariumProcessor& processor, juce::Component* parent) 4 | : proc(processor), parentComponent(parent) 5 | { 6 | setName("Settings Panel"); 7 | 8 | // Add theme selector 9 | themeLabel.setText("Theme:", juce::dontSendNotification); 10 | themeLabel.setJustificationType(juce::Justification::right); 11 | themeSelector.addItem("Default Theme", 1); 12 | themeSelector.addItem("Dark Theme", 2); 13 | themeSelector.addItem("Light Theme", 3); 14 | themeSelector.setSelectedId(1, juce::dontSendNotification); 15 | 16 | // Add show tooltips toggle 17 | showTooltipsToggle.setButtonText("Show Tooltips"); 18 | showTooltipsToggle.setToggleState(true, juce::dontSendNotification); 19 | 20 | // Add enable animations toggle 21 | enableAnimationsToggle.setButtonText("Enable Animations"); 22 | enableAnimationsToggle.setToggleState(true, juce::dontSendNotification); 23 | 24 | // Add UI scale slider 25 | uiScaleSlider.setRange(0.75, 1.5, 0.05); 26 | uiScaleSlider.setValue(1.0, juce::dontSendNotification); 27 | uiScaleSlider.setSliderStyle(juce::Slider::SliderStyle::LinearHorizontal); 28 | uiScaleSlider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 50, 20); 29 | 30 | uiScaleLabel.setText("UI Scale:", juce::dontSendNotification); 31 | uiScaleLabel.setJustificationType(juce::Justification::right); 32 | 33 | // Add close button 34 | closeButton.onClick = [this]() { 35 | if (onCloseButtonClick) 36 | onCloseButtonClick(); 37 | else 38 | hide(); 39 | }; 40 | 41 | // Add all components to the panel 42 | addAndMakeVisible(themeLabel); 43 | addAndMakeVisible(themeSelector); 44 | addAndMakeVisible(showTooltipsToggle); 45 | addAndMakeVisible(enableAnimationsToggle); 46 | addAndMakeVisible(uiScaleLabel); 47 | addAndMakeVisible(uiScaleSlider); 48 | addAndMakeVisible(closeButton); 49 | 50 | // Set look and feel from processor 51 | setLookAndFeel(processor.lf.get()); 52 | 53 | // Set initial size 54 | setSize(350, 220); 55 | } 56 | 57 | SettingsPanel::~SettingsPanel() 58 | { 59 | setLookAndFeel(nullptr); 60 | } 61 | 62 | void SettingsPanel::paint(juce::Graphics& g) 63 | { 64 | // Draw the panel background 65 | g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId).darker(0.1f)); 66 | 67 | // Draw panel title 68 | g.setColour(juce::Colours::white); 69 | g.setFont(juce::FontOptions(20.0f, juce::Font::bold)); 70 | g.drawText("Settings", getLocalBounds().removeFromTop(40), juce::Justification::centred, true); 71 | 72 | // Draw border around the panel 73 | g.setColour(juce::Colours::white.withAlpha(0.2f)); 74 | g.drawRoundedRectangle(getLocalBounds().toFloat().reduced(1.0f), 5.0f, 1.0f); 75 | } 76 | 77 | void SettingsPanel::resized() 78 | { 79 | 80 | // Calculate the content area with a little more room for the title area 81 | auto bounds = getLocalBounds().reduced(20); 82 | 83 | // Title area 84 | bounds.removeFromTop(30); // Space for title 85 | 86 | // Theme selector row 87 | auto themeRow = bounds.removeFromTop(24); 88 | themeLabel.setBounds(themeRow.removeFromLeft(80)); 89 | themeSelector.setBounds(themeRow); 90 | 91 | bounds.removeFromTop(10); // spacing 92 | 93 | // Toggle switches 94 | showTooltipsToggle.setBounds(bounds.removeFromTop(24)); 95 | 96 | bounds.removeFromTop(10); // spacing 97 | 98 | enableAnimationsToggle.setBounds(bounds.removeFromTop(24)); 99 | 100 | bounds.removeFromTop(10); // spacing 101 | 102 | // UI scale row 103 | auto scaleRow = bounds.removeFromTop(24); 104 | uiScaleLabel.setBounds(scaleRow.removeFromLeft(80)); 105 | uiScaleSlider.setBounds(scaleRow); 106 | 107 | bounds.removeFromTop(20); // spacing 108 | 109 | // Close button 110 | closeButton.setBounds(bounds.removeFromTop(30).withSizeKeepingCentre(120, 30)); 111 | } 112 | 113 | void SettingsPanel::show() 114 | { 115 | // Create a blurred background image using BlurryComp from gin 116 | blur = std::make_unique(parentComponent->createComponentSnapshot(parentComponent->getLocalBounds())); 117 | blur->setBounds(parentComponent->getLocalBounds()); 118 | 119 | // Add blurred background to parent component 120 | parentComponent->addAndMakeVisible(*blur); 121 | 122 | // Center the panel in the parent component 123 | setBounds(blur->getLocalBounds().withSizeKeepingCentre(getWidth(), getHeight())); 124 | 125 | // Add the settings panel on top of the blur 126 | blur->addAndMakeVisible(this); 127 | 128 | // Bring to front to ensure it's visible 129 | toFront(true); 130 | } 131 | 132 | void SettingsPanel::hide() 133 | { 134 | // Remove our parent (the blur component) from its parent 135 | if (blur != nullptr) 136 | { 137 | if (blur->getParentComponent() != nullptr) 138 | blur->getParentComponent()->removeChildComponent(blur.get()); 139 | 140 | blur = nullptr; 141 | } 142 | } 143 | 144 | 145 | -------------------------------------------------------------------------------- /plugin/Source/dsp/airwindows/GrindAmp.h: -------------------------------------------------------------------------------- 1 | /* ======================================== 2 | * GrindAmp - GrindAmp.h 3 | * Created 8/12/11 by SPIAdmin 4 | * Copyright (c) 2011 __MyCompanyName__, Airwindows uses the MIT license 5 | * ======================================== */ 6 | 7 | #pragma once 8 | 9 | #include "FXBase.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | class GrindAmp final : public AudioEffectX 16 | { 17 | public: 18 | enum { 19 | kParamA = 0, 20 | kParamB = 1, 21 | kParamC = 2, 22 | kParamD = 3, 23 | kNumParameters = 4 24 | }; // 25 | 26 | static constexpr int kNumPrograms = 0; 27 | static constexpr int kNumInputs = 2; 28 | static constexpr int kNumOutputs = 2; 29 | static constexpr unsigned long kUniqueId = 'grda'; //Change this to what the AU identity is! 30 | 31 | GrindAmp(audioMasterCallback audioMaster); 32 | ~GrindAmp(); 33 | void reset(); 34 | bool getEffectName(char* name); // The plug-in name 35 | VstPlugCategory getPlugCategory(); // The general category for the plug-in 36 | bool getProductString(char* text); // This is a unique plug-in string provided by Steinberg 37 | bool getVendorString(char* text); // Vendor info 38 | VstInt32 getVendorVersion(); // Version number 39 | void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames); 40 | void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames); 41 | void getProgramName(char *name); // read the name from the host 42 | void setProgramName(char *name); // changes the name of the preset displayed in the host 43 | VstInt32 getChunk (void** data, bool isPreset); 44 | VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset); 45 | float getParameter(VstInt32 index); // get the parameter value at the specified index 46 | void setParameter(VstInt32 index, float value); // set the parameter at index to value 47 | void getParameterLabel(VstInt32 index, char *text); // label for the parameter (eg dB) 48 | static void getParameterName(VstInt32 index, char *text); // name of the parameter 49 | void getParameterDisplay(VstInt32 index, char *text); // text description of the current value 50 | VstInt32 canDo(char *text); 51 | private: 52 | char _programName[kVstMaxProgNameLen + 1]; 53 | std::set< std::string > _canDo; 54 | 55 | double smoothAL; 56 | double smoothBL; 57 | double smoothCL; 58 | double smoothDL; 59 | double smoothEL; 60 | double smoothFL; 61 | double smoothGL; 62 | double smoothHL; 63 | double smoothIL; 64 | double smoothJL; 65 | double smoothKL; 66 | double secondAL; 67 | double secondBL; 68 | double secondCL; 69 | double secondDL; 70 | double secondEL; 71 | double secondFL; 72 | double secondGL; 73 | double secondHL; 74 | double secondIL; 75 | double secondJL; 76 | double secondKL; 77 | double thirdAL; 78 | double thirdBL; 79 | double thirdCL; 80 | double thirdDL; 81 | double thirdEL; 82 | double thirdFL; 83 | double thirdGL; 84 | double thirdHL; 85 | double thirdIL; 86 | double thirdJL; 87 | double thirdKL; 88 | double iirSampleAL; 89 | double iirSampleBL; 90 | double iirSampleCL; 91 | double iirSampleDL; 92 | double iirSampleEL; 93 | double iirSampleFL; 94 | double iirSampleGL; 95 | double iirSampleHL; 96 | double iirSampleIL; 97 | double iirLowpassL; 98 | double iirSubL; 99 | double storeSampleL; //amp 100 | 101 | double smoothAR; 102 | double smoothBR; 103 | double smoothCR; 104 | double smoothDR; 105 | double smoothER; 106 | double smoothFR; 107 | double smoothGR; 108 | double smoothHR; 109 | double smoothIR; 110 | double smoothJR; 111 | double smoothKR; 112 | double secondAR; 113 | double secondBR; 114 | double secondCR; 115 | double secondDR; 116 | double secondER; 117 | double secondFR; 118 | double secondGR; 119 | double secondHR; 120 | double secondIR; 121 | double secondJR; 122 | double secondKR; 123 | double thirdAR; 124 | double thirdBR; 125 | double thirdCR; 126 | double thirdDR; 127 | double thirdER; 128 | double thirdFR; 129 | double thirdGR; 130 | double thirdHR; 131 | double thirdIR; 132 | double thirdJR; 133 | double thirdKR; 134 | double iirSampleAR; 135 | double iirSampleBR; 136 | double iirSampleCR; 137 | double iirSampleDR; 138 | double iirSampleER; 139 | double iirSampleFR; 140 | double iirSampleGR; 141 | double iirSampleHR; 142 | double iirSampleIR; 143 | double iirLowpassR; 144 | double iirSubR; 145 | double storeSampleR; //amp 146 | 147 | double bL[90]; 148 | double lastCabSampleL; 149 | double smoothCabAL; 150 | double smoothCabBL; //cab 151 | 152 | double bR[90]; 153 | double lastCabSampleR; 154 | double smoothCabAR; 155 | double smoothCabBR; //cab 156 | 157 | double lastRefL[10]; 158 | double lastRefR[10]; 159 | int cycle; //undersampling 160 | 161 | enum { 162 | fix_freq, 163 | fix_reso, 164 | fix_a0, 165 | fix_a1, 166 | fix_a2, 167 | fix_b1, 168 | fix_b2, 169 | fix_sL1, 170 | fix_sL2, 171 | fix_sR1, 172 | fix_sR2, 173 | fix_total 174 | }; //fixed frequency biquad filter for ultrasonics, stereo 175 | double fixA[fix_total]; 176 | double fixB[fix_total]; 177 | double fixC[fix_total]; 178 | double fixD[fix_total]; 179 | double fixE[fix_total]; 180 | double fixF[fix_total]; //filtering 181 | 182 | uint32_t fpdL; 183 | uint32_t fpdR; 184 | //default stuff 185 | 186 | float A; 187 | float B; 188 | float C; 189 | float D; 190 | }; 191 | -------------------------------------------------------------------------------- /plugin/Source/util/RandomLFO.cpp: -------------------------------------------------------------------------------- 1 | #include "RandomLFO.h" 2 | 3 | RandomLFO::RandomLFO(gin::ModVoice* voice, bool stereo) : voice(voice), stereo(stereo), 4 | leftState(sideStates[0]), 5 | rightState(sideStates[1]) 6 | { 7 | } 8 | 9 | RandomLFO::RandomLFO(RandomLFOParams params, bool stereo) : params(params), stereo(stereo), 10 | leftState(sideStates[0]), 11 | rightState(sideStates[1]) 12 | { 13 | } 14 | 15 | RandomLFO::RandomLFO(gin::ModVoice* voice, RandomLFOParams params, bool stereo) : params(params), stereo(stereo), 16 | leftState(sideStates[0]), 17 | rightState(sideStates[1]) 18 | { 19 | } 20 | 21 | 22 | void RandomLFO::prepare(const juce::dsp::ProcessSpec& spec) 23 | { 24 | this->sampleRate = spec.sampleRate; 25 | this->oneOverSampleRate = 1.0f / sampleRate; 26 | centerState.rng.setSeed(params.seed); 27 | leftState.rng.setSeed(centerState.rng.nextInt()); 28 | rightState.rng.setSeed(centerState.rng.nextInt()); 29 | this->reset(); 30 | } 31 | 32 | void RandomLFO::updateParameters(float frequency) 33 | { 34 | this->rate = frequency; 35 | this->sync = params.sync->isOn(); 36 | this->mode = static_cast(static_cast(params.mode->getProcValue())); 37 | this->depth = voice->getValue(params.depth); 38 | this->offset = voice->getValue(params.offset); 39 | this->smooth = voice->getValue(params.smooth); 40 | this->chaos = voice->getValue(params.chaos); 41 | this->jitter = voice->getValue(params.jitter); 42 | this->stereoAmount = voice->getValue(params.stereo); 43 | } 44 | 45 | void RandomLFO::updateParametersMono(gin::ModMatrix& matrix, float frequency) 46 | { 47 | this->rate = frequency; 48 | this->sync = params.sync->isOn(); 49 | this->mode = static_cast(static_cast(params.mode->getProcValue())); 50 | this->depth = matrix.getValue(params.depth); 51 | this->offset = matrix.getValue(params.offset); 52 | this->smooth = matrix.getValue(params.smooth); 53 | this->chaos = matrix.getValue(params.chaos); 54 | this->jitter = matrix.getValue(params.jitter); 55 | this->stereoAmount = matrix.getValue(params.stereo); 56 | } 57 | 58 | void RandomLFO::reset() 59 | { 60 | currentPhase = 0; 61 | centerState.lastRandomValue = centerState.rng.nextFloat(); 62 | centerState.nextRandomValue = centerState.rng.nextFloat(); 63 | leftState.lastRandomValue = leftState.rng.nextFloat(); 64 | leftState.nextRandomValue = leftState.rng.nextFloat(); 65 | rightState.lastRandomValue = rightState.rng.nextFloat(); 66 | rightState.nextRandomValue = rightState.rng.nextFloat(); 67 | } 68 | 69 | void RandomLFO::noteOn(float phase) 70 | { 71 | } 72 | 73 | void RandomLFO::processInternal(int numSamples, RandomState& state) 74 | { 75 | float min = std::max(0.0f, state.lastRandomValue - state.lastRandomValue * chaos); 76 | float max = std::min(1.0f, state.lastRandomValue + (1.0f - state.lastRandomValue) * chaos); 77 | 78 | static constexpr uint32_t MAX_UINT = std::numeric_limits::max(); 79 | state.lastRandomValue = state.nextRandomValue; 80 | 81 | // Convert signed int to unsigned int before casting to float 82 | uint32_t unsignedRandom = static_cast(state.rng.nextInt()) + 0x80000000; 83 | state.nextRandomValue = min + (static_cast(unsignedRandom) / static_cast(MAX_UINT)) * (max - min); 84 | 85 | jassert(state.lastRandomValue >= 0.0f && state.lastRandomValue <= 1.0f); 86 | jassert(state.nextRandomValue >= 0.0f && state.nextRandomValue <= 1.0f); 87 | } 88 | 89 | void RandomLFO::process(int numSamples) 90 | { 91 | phaseDelta = rate * oneOverSampleRate * numSamples; 92 | currentPhase += phaseDelta; 93 | if (currentPhase >= 1.0f) 94 | { 95 | currentPhase -= 1.0f; 96 | processInternal(numSamples, centerState); 97 | processInternal(numSamples, leftState); 98 | processInternal(numSamples, rightState); 99 | } 100 | 101 | auto modifiedPhase = currentPhase; 102 | modifiedPhase = smooth == 0 ? 1 : juce::jmin(1.0f, ((1.0f / smooth) * currentPhase)); 103 | centerState.currentRandomValue = perlinInterpolate(centerState.lastRandomValue, centerState.nextRandomValue, 104 | modifiedPhase); 105 | leftState.currentRandomValue = perlinInterpolate(leftState.lastRandomValue, leftState.nextRandomValue, 106 | modifiedPhase); 107 | rightState.currentRandomValue = perlinInterpolate(rightState.lastRandomValue, rightState.nextRandomValue, 108 | modifiedPhase); 109 | 110 | centerState.atomicState.store(centerState.currentRandomValue, std::memory_order_relaxed); 111 | leftState.atomicState.store(leftState.currentRandomValue, std::memory_order_relaxed); 112 | rightState.atomicState.store(rightState.currentRandomValue, std::memory_order_relaxed); 113 | } 114 | 115 | float RandomLFO::getOutput() 116 | { 117 | return centerState.currentRandomValue * 2.0f - 1.0f; 118 | } 119 | 120 | float RandomLFO::getOutput(int channel) 121 | { 122 | jassert(channel == 0 || channel == 1); 123 | const float output = sideStates[channel].currentRandomValue * stereoAmount + centerState.currentRandomValue * (1.0f 124 | - stereoAmount); 125 | return output * 2.0f - 1.0f; 126 | } 127 | 128 | float RandomLFO::perlinInterpolate(float from, float to, float t) 129 | { 130 | t = t * t * (3.0f - 2.0f * t); 131 | return (from * (1.0f - t) + to * t); 132 | } 133 | -------------------------------------------------------------------------------- /plugin/Source/dsp/airwindows/BassAmp.h: -------------------------------------------------------------------------------- 1 | /* ======================================== 2 | * BassAmp - BassAmp.h 3 | * Created 8/12/11 by SPIAdmin 4 | * Copyright (c) 2011 __MyCompanyName__, Airwindows uses the MIT license 5 | * ======================================== */ 6 | 7 | #pragma once 8 | 9 | #include "FXBase.h" 10 | #include 11 | #include 12 | #include 13 | 14 | //Change this to what the AU identity is! 15 | 16 | class BassAmp final : 17 | public AudioEffectX 18 | { 19 | public: 20 | enum { 21 | kParamA = 0, 22 | kParamB = 1, 23 | kParamC = 2, 24 | kParamD = 3, 25 | kNumParameters = 4 26 | }; // 27 | 28 | static constexpr int kNumPrograms = 0; 29 | static constexpr int kNumInputs = 2; 30 | static constexpr int kNumOutputs = 2; 31 | static constexpr unsigned long kUniqueId = 'basa'; 32 | 33 | BassAmp(audioMasterCallback audioMaster); 34 | ~BassAmp(); 35 | void reset(); 36 | virtual bool getEffectName(char* name); // The plug-in name 37 | virtual VstPlugCategory getPlugCategory(); // The general category for the plug-in 38 | virtual bool getProductString(char* text); // This is a unique plug-in string provided by Steinberg 39 | virtual bool getVendorString(char* text); // Vendor info 40 | virtual VstInt32 getVendorVersion(); // Version number 41 | virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames); 42 | virtual void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames); 43 | virtual void getProgramName(char *name); // read the name from the host 44 | virtual void setProgramName(char *name); // changes the name of the preset displayed in the host 45 | virtual VstInt32 getChunk (void** data, bool isPreset); 46 | virtual VstInt32 setChunk (void* data, VstInt32 byteSize, bool isPreset); 47 | virtual float getParameter(VstInt32 index); // get the parameter value at the specified index 48 | virtual void setParameter(VstInt32 index, float value); // set the parameter at index to value 49 | virtual void getParameterLabel(VstInt32 index, char *text); // label for the parameter (eg dB) 50 | static void getParameterName(VstInt32 index, char *text); // name of the parameter 51 | virtual void getParameterDisplay(VstInt32 index, char *text); // text description of the current value 52 | virtual VstInt32 canDo(char *text); 53 | private: 54 | char _programName[kVstMaxProgNameLen + 1]; 55 | std::set< std::string > _canDo; 56 | 57 | double LataLast6Sample; 58 | double LataLast5Sample; 59 | double LataLast4Sample; 60 | double LataLast3Sample; 61 | double LataLast2Sample; 62 | double LataLast1Sample; 63 | double LataHalfwaySample; 64 | double LataHalfDrySample; 65 | double LataHalfDiffSample; 66 | double LataLastDiffSample; 67 | double LataDrySample; 68 | double LataDiffSample; 69 | double LataPrevDiffSample; 70 | 71 | double LiirDriveSampleA; 72 | double LiirDriveSampleB; 73 | double LiirDriveSampleC; 74 | double LiirDriveSampleD; 75 | double LiirDriveSampleE; 76 | double LiirDriveSampleF; 77 | 78 | bool LWasNegative; 79 | bool LSubOctave; 80 | double LiirHeadBumpA; 81 | double LiirHeadBumpB; 82 | double LiirHeadBumpC; 83 | 84 | double LiirSubBumpA; 85 | double LiirSubBumpB; 86 | double LiirSubBumpC; 87 | 88 | double LiirSampleA; 89 | double LiirSampleB; 90 | double LiirSampleC; 91 | double LiirSampleD; 92 | double LiirSampleE; 93 | double LiirSampleF; 94 | double LiirSampleG; 95 | double LiirSampleH; 96 | double LiirSampleI; 97 | double LiirSampleJ; 98 | double LiirSampleK; 99 | double LiirSampleL; 100 | double LiirSampleM; 101 | double LiirSampleN; 102 | double LiirSampleO; 103 | double LiirSampleP; 104 | double LiirSampleQ; 105 | double LiirSampleR; 106 | double LiirSampleS; 107 | double LiirSampleT; 108 | double LiirSampleU; 109 | double LiirSampleV; 110 | double LiirSampleW; 111 | double LiirSampleX; 112 | double LiirSampleY; 113 | double LiirSampleZ; 114 | 115 | double RataLast6Sample; 116 | double RataLast5Sample; 117 | double RataLast4Sample; 118 | double RataLast3Sample; 119 | double RataLast2Sample; 120 | double RataLast1Sample; 121 | double RataHalfwaySample; 122 | double RataHalfDrySample; 123 | double RataHalfDiffSample; 124 | double RataLastDiffSample; 125 | double RataDrySample; 126 | double RataDiffSample; 127 | double RataPrevDiffSample; 128 | 129 | double RiirDriveSampleA; 130 | double RiirDriveSampleB; 131 | double RiirDriveSampleC; 132 | double RiirDriveSampleD; 133 | double RiirDriveSampleE; 134 | double RiirDriveSampleF; 135 | 136 | bool RWasNegative; 137 | bool RSubOctave; 138 | double RiirHeadBumpA; 139 | double RiirHeadBumpB; 140 | double RiirHeadBumpC; 141 | 142 | double RiirSubBumpA; 143 | double RiirSubBumpB; 144 | double RiirSubBumpC; 145 | 146 | double RiirSampleA; 147 | double RiirSampleB; 148 | double RiirSampleC; 149 | double RiirSampleD; 150 | double RiirSampleE; 151 | double RiirSampleF; 152 | double RiirSampleG; 153 | double RiirSampleH; 154 | double RiirSampleI; 155 | double RiirSampleJ; 156 | double RiirSampleK; 157 | double RiirSampleL; 158 | double RiirSampleM; 159 | double RiirSampleN; 160 | double RiirSampleO; 161 | double RiirSampleP; 162 | double RiirSampleQ; 163 | double RiirSampleR; 164 | double RiirSampleS; 165 | double RiirSampleT; 166 | double RiirSampleU; 167 | double RiirSampleV; 168 | double RiirSampleW; 169 | double RiirSampleX; 170 | double RiirSampleY; 171 | double RiirSampleZ; 172 | 173 | double ataK1; 174 | double ataK2; 175 | double ataK3; 176 | double ataK4; 177 | double ataK5; 178 | double ataK6; 179 | double ataK7; 180 | double ataK8; //end antialiasing variables 181 | 182 | bool flip; //drive things 183 | int bflip; 184 | 185 | uint32_t fpdL; 186 | uint32_t fpdR; 187 | //default stuff 188 | 189 | float A; 190 | float B; 191 | float C; 192 | float D; 193 | }; -------------------------------------------------------------------------------- /plugin/Source/ResonatorSynth.cpp: -------------------------------------------------------------------------------- 1 | #include "ResonatorSynth.h" 2 | #include "ResonatorVoice.h" 3 | #include "PluginProcessor.h" 4 | 5 | ResonatorSynth::ResonatorSynth(GlobalState& state, SynthParams params) : state(state), params(params), effectChain(params.effectChainParams) 6 | { 7 | monoMSEGs.clear(); 8 | msegData.clear(); 9 | 10 | for (int i = 0; i < NUM_LFOS; i++) 11 | { 12 | monoLFOs[i].params = params.lfoParams[i]; 13 | } 14 | 15 | for (int i = 0; i < NUM_RANDOMS; i++) 16 | { 17 | monoRandomLFOs[i].params = params.randomLfoParams[i]; 18 | } 19 | 20 | for (int i = 0; i < NUM_MSEGS; i++) 21 | { 22 | auto mseg = StereoMSEGWrapper(params.msegParams[i]); 23 | monoMSEGs.add(mseg); 24 | } 25 | } 26 | void ResonatorSynth::prepare(const juce::dsp::ProcessSpec& spec) 27 | { 28 | setCurrentPlaybackSampleRate(spec.sampleRate); 29 | 30 | for (auto* v : voices) 31 | { 32 | dynamic_cast(v)->prepare(spec); 33 | } 34 | 35 | for (auto & monoLFO : monoLFOs) 36 | { 37 | monoLFO.reset(); 38 | monoLFO.prepare(spec); 39 | } 40 | 41 | for (auto & monoRandomLFO : monoRandomLFOs) 42 | { 43 | monoRandomLFO.reset(); 44 | monoRandomLFO.prepare(spec); 45 | } 46 | 47 | for (int i = 0; i < NUM_MSEGS; i++) 48 | { 49 | monoMSEGs.getReference(i).prepare(spec); 50 | } 51 | effectChain.reset(); 52 | effectChain.prepare(spec); 53 | updateParameters(); 54 | state.modMatrix.snapParams(); 55 | 56 | } 57 | 58 | void ResonatorSynth::updateParameters() 59 | { 60 | int rawIndex = static_cast(params.soloResonator->getProcValue()); 61 | state.soloActive = rawIndex > -1.0f; 62 | if(state.soloActive) 63 | { 64 | state.soloBankIndex = std::floor(rawIndex / NUM_RESONATORS); 65 | state.soloResonatorIndex = rawIndex % NUM_RESONATORS; 66 | } 67 | state.polyFX = params.globalParams.polyEffectChain->isOn(); 68 | for (int i = 0; i < NUM_LFOS; i++) 69 | { 70 | if (params.lfoParams[i].enabled->isOn()) 71 | { 72 | float freq = 0; 73 | if (params.lfoParams[i].sync->getProcValue() > 0.0f) 74 | freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t(params.lfoParams[i].beat->getProcValue())]. 75 | toSeconds(state.playHead); 76 | else 77 | freq = state.modMatrix.getValue(params.lfoParams[i].rate); 78 | monoLFOs[i].updateParameters(state.modMatrix, freq); 79 | monoLFOs[i].process(currentBlockSize); 80 | state.modMatrix.setMonoValue(state.modSrcMonoLFO[i], monoLFOs[i].getOutput(0), 0); 81 | state.modMatrix.setMonoValue(state.modSrcMonoLFO[i], monoLFOs[i].getOutput(1), 1); 82 | } 83 | else 84 | { 85 | state.modMatrix.setMonoValue(state.modSrcMonoLFO[i], 0, 0); 86 | state.modMatrix.setMonoValue(state.modSrcMonoLFO[i], 0, 1); 87 | } 88 | } 89 | 90 | for (int i = 0; i < NUM_RANDOMS; i++) 91 | { 92 | if (params.randomLfoParams[i].enabled->isOn()) 93 | { 94 | float rate = 0; 95 | if (params.randomLfoParams[i].sync->getProcValue() > 0.0f) 96 | rate = 1.0f / gin::NoteDuration::getNoteDurations()[size_t( 97 | params.randomLfoParams[i].beat->getProcValue())]. 98 | toSeconds(state.playHead); 99 | else 100 | rate = state.modMatrix.getValue(params.randomLfoParams[i].rate); 101 | monoRandomLFOs[i].updateParametersMono(state.modMatrix, rate); 102 | monoRandomLFOs[i].process(currentBlockSize); 103 | state.modMatrix.setMonoValue(state.modSrcMonoRND[i], monoRandomLFOs[i].getOutput(0), 0); 104 | state.modMatrix.setMonoValue(state.modSrcMonoRND[i], monoRandomLFOs[i].getOutput(1), 1); 105 | } 106 | } 107 | 108 | for (int i = 0; i < NUM_MSEGS; i++) 109 | { 110 | if (params.msegParams[i].enabled->isOn()) 111 | { 112 | float rate = 0; 113 | if (params.msegParams[i].sync->getProcValue() > 0.0f) 114 | rate = 1.0f / gin::NoteDuration::getNoteDurations()[size_t(params.msegParams[i].beat->getProcValue())]. 115 | toSeconds(state.playHead); 116 | else 117 | rate = state.modMatrix.getValue(params.msegParams[i].rate); 118 | monoMSEGs.getReference(i).updateParameters(state.modMatrix, rate); 119 | monoMSEGs.getReference(i).process(currentBlockSize); 120 | state.modMatrix.setMonoValue(state.modSrcMonoMSEG[i], monoMSEGs.getReference(i).getOutput(0), 0); 121 | state.modMatrix.setMonoValue(state.modSrcMonoMSEG[i], monoMSEGs.getReference(i).getOutput(1), 1); 122 | } 123 | else 124 | { 125 | state.modMatrix.setMonoValue(state.modSrcMonoMSEG[i], 0, 0); 126 | state.modMatrix.setMonoValue(state.modSrcMonoMSEG[i], 0, 1); 127 | } 128 | } 129 | 130 | for(int i = 0; i < NUM_MACROS; i++) 131 | { 132 | state.modMatrix.setMonoValue(state.modSrcMacro[i], state.modMatrix.getValue(params.macroParams[i]), 0); 133 | state.modMatrix.setMonoValue(state.modSrcMacro[i], state.modMatrix.getValue(params.macroParams[i]), 1); 134 | } 135 | 136 | if (! state.polyFX) 137 | { 138 | effectChain.updateParameters(state.modMatrix, state.playHead); 139 | } 140 | } 141 | 142 | void ResonatorSynth::renderNextSubBlock(juce::AudioBuffer& outputAudio, int startSample, int numSamples) 143 | { 144 | currentBlockSize = numSamples; 145 | updateParameters(); 146 | Synthesiser::renderNextSubBlock(outputAudio, startSample, numSamples); 147 | juce::dsp::AudioBlock block = juce::dsp::AudioBlock(outputAudio).getSubBlock(startSample, numSamples); 148 | 149 | if (! state.polyFX ) 150 | { 151 | effectChain.process(block); 152 | } 153 | } 154 | 155 | void ResonatorSynth::panic() 156 | { 157 | //kill voices and reset 158 | for (auto v : voices) 159 | { 160 | stopVoiceFastKill(v, v->getCurrentlyPlayingNote(), false); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /plugin/Source/dsp/airwindows/DeRez2.cpp: -------------------------------------------------------------------------------- 1 | /* ======================================== 2 | * DeRez2 - DeRez2.h 3 | * Copyright (c) 2016 airwindows, Airwindows uses the MIT license 4 | * ======================================== */ 5 | 6 | #ifndef __DeRez2_H 7 | #include "DeRez2.h" 8 | #include 9 | #endif 10 | 11 | JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-conversion", "-Wimplicit-float-conversion", "-Wunused-parameter") 12 | 13 | DeRez2::DeRez2(audioMasterCallback audioMaster) : 14 | AudioEffectX(audioMaster, kNumPrograms, kNumParameters) 15 | { 16 | A = 1.0; 17 | B = 1.0; 18 | C = 1.0; 19 | D = 1.0; 20 | lastSampleL = 0.0; 21 | heldSampleL = 0.0; 22 | lastDrySampleL = 0.0; 23 | lastOutputSampleL = 0.0; 24 | 25 | lastSampleR = 0.0; 26 | heldSampleR = 0.0; 27 | lastDrySampleR = 0.0; 28 | lastOutputSampleR = 0.0; 29 | 30 | position = 0.0; 31 | incrementA = 0.0; 32 | incrementB = 0.0; 33 | fpdL = 1.0; while (fpdL < 16386) fpdL = rand()*UINT32_MAX; 34 | fpdR = 1.0; while (fpdR < 16386) fpdR = rand()*UINT32_MAX; 35 | //this is reset: values being initialized only once. Startup values, whatever they are. 36 | 37 | _canDo.insert("plugAsChannelInsert"); // plug-in can be used as a channel insert effect. 38 | _canDo.insert("plugAsSend"); // plug-in can be used as a send effect. 39 | _canDo.insert("x2in2out"); 40 | setNumInputs(kNumInputs); 41 | setNumOutputs(kNumOutputs); 42 | setUniqueID(kUniqueId); 43 | canProcessReplacing(); // supports output replacing 44 | canDoubleReplacing(); // supports double precision processing 45 | programsAreChunks(true); 46 | vst_strncpy (_programName, "Default", kVstMaxProgNameLen); // default program name 47 | } 48 | 49 | DeRez2::~DeRez2() {} 50 | VstInt32 DeRez2::getVendorVersion () {return 1000;} 51 | void DeRez2::setProgramName(char *name) {vst_strncpy (_programName, name, kVstMaxProgNameLen);} 52 | void DeRez2::getProgramName(char *name) {vst_strncpy (name, _programName, kVstMaxProgNameLen);} 53 | //airwindows likes to ignore this stuff. Make your own programs, and make a different plugin rather than 54 | //trying to do versioning and preventing people from using older versions. Maybe they like the old one! 55 | 56 | void DeRez2::reset() 57 | { 58 | A = 1.0; 59 | B = 1.0; 60 | C = 1.0; 61 | D = 1.0; 62 | lastSampleL = 0.0; 63 | heldSampleL = 0.0; 64 | lastDrySampleL = 0.0; 65 | lastOutputSampleL = 0.0; 66 | 67 | lastSampleR = 0.0; 68 | heldSampleR = 0.0; 69 | lastDrySampleR = 0.0; 70 | lastOutputSampleR = 0.0; 71 | 72 | position = 0.0; 73 | incrementA = 0.0; 74 | incrementB = 0.0; 75 | fpdL = 1.0; while (fpdL < 16386) fpdL = rand()*UINT32_MAX; 76 | fpdR = 1.0; while (fpdR < 16386) fpdR = rand()*UINT32_MAX; 77 | } 78 | 79 | static float pinParameter(float data) 80 | { 81 | if (data < 0.0f) return 0.0f; 82 | if (data > 1.0f) return 1.0f; 83 | return data; 84 | } 85 | 86 | VstInt32 DeRez2::getChunk (void** data, bool isPreset) 87 | { 88 | float *chunkData = (float *)calloc(kNumParameters, sizeof(float)); 89 | chunkData[0] = A; 90 | chunkData[1] = B; 91 | chunkData[2] = C; 92 | chunkData[3] = D; 93 | /* Note: The way this is set up, it will break if you manage to save settings on an Intel 94 | machine and load them on a PPC Mac. However, it's fine if you stick to the machine you 95 | started with. */ 96 | 97 | *data = chunkData; 98 | return kNumParameters * sizeof(float); 99 | } 100 | 101 | VstInt32 DeRez2::setChunk (void* data, VstInt32 byteSize, bool isPreset) 102 | { 103 | float *chunkData = (float *)data; 104 | A = pinParameter(chunkData[0]); 105 | B = pinParameter(chunkData[1]); 106 | C = pinParameter(chunkData[2]); 107 | D = pinParameter(chunkData[3]); 108 | /* We're ignoring byteSize as we found it to be a filthy liar */ 109 | 110 | /* calculate any other fields you need here - you could copy in 111 | code from setParameter() here. */ 112 | return 0; 113 | } 114 | 115 | void DeRez2::setParameter(VstInt32 index, float value) { 116 | switch (index) { 117 | case kParamA: A = value; break; 118 | case kParamB: B = value; break; 119 | case kParamC: C = value; break; 120 | case kParamD: D = value; break; 121 | default: throw; // unknown parameter, shouldn't happen! 122 | } 123 | } 124 | 125 | float DeRez2::getParameter(VstInt32 index) { 126 | switch (index) { 127 | case kParamA: return A; break; 128 | case kParamB: return B; break; 129 | case kParamC: return C; break; 130 | case kParamD: return D; break; 131 | default: break; // unknown parameter, shouldn't happen! 132 | } return 0.0; //we only need to update the relevant name, this is simple to manage 133 | } 134 | 135 | void DeRez2::getParameterName(VstInt32 index, char *text) { 136 | switch (index) { 137 | case kParamA: vst_strncpy (text, "Rate", kVstMaxParamStrLen); break; 138 | case kParamB: vst_strncpy (text, "Rez", kVstMaxParamStrLen); break; 139 | case kParamC: vst_strncpy (text, "Hard", kVstMaxParamStrLen); break; 140 | case kParamD: vst_strncpy (text, "Mix", kVstMaxParamStrLen); break; 141 | default: break; // unknown parameter, shouldn't happen! 142 | } //this is our labels for displaying in the VST host 143 | } 144 | 145 | void DeRez2::getParameterDisplay(VstInt32 index, char *text) { 146 | switch (index) { 147 | case kParamA: float2string (A, text, kVstMaxParamStrLen); break; 148 | case kParamB: float2string (B, text, kVstMaxParamStrLen); break; 149 | case kParamC: float2string (C, text, kVstMaxParamStrLen); break; 150 | case kParamD: float2string (D, text, kVstMaxParamStrLen); break; 151 | default: break; // unknown parameter, shouldn't happen! 152 | } //this displays the values and handles 'popups' where it's discrete choices 153 | } 154 | 155 | void DeRez2::getParameterLabel(VstInt32 index, char *text) { 156 | switch (index) { 157 | case kParamA: vst_strncpy (text, "", kVstMaxParamStrLen); break; 158 | case kParamB: vst_strncpy (text, "", kVstMaxParamStrLen); break; 159 | case kParamC: vst_strncpy (text, "", kVstMaxParamStrLen); break; 160 | case kParamD: vst_strncpy (text, "", kVstMaxParamStrLen); break; 161 | default: break; // unknown parameter, shouldn't happen! 162 | } 163 | } 164 | 165 | VstInt32 DeRez2::canDo(char *text) 166 | { return (_canDo.find(text) == _canDo.end()) ? -1: 1; } // 1 = yes, -1 = no, 0 = don't know 167 | 168 | bool DeRez2::getEffectName(char* name) { 169 | vst_strncpy(name, "DeRez2", kVstMaxProductStrLen); return true; 170 | } 171 | 172 | VstPlugCategory DeRez2::getPlugCategory() {return kPlugCategEffect;} 173 | 174 | bool DeRez2::getProductString(char* text) { 175 | vst_strncpy (text, "airwindows DeRez2", kVstMaxProductStrLen); return true; 176 | } 177 | 178 | bool DeRez2::getVendorString(char* text) { 179 | vst_strncpy (text, "airwindows", kVstMaxVendorStrLen); return true; 180 | } 181 | -------------------------------------------------------------------------------- /plugin/Resources/Presets_old/Harmonious Ascendence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 11 | 13 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 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 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /plugin/Resources/Presets_old/Simple Bass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 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 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /plugin/Source/dsp/ResonariumEffectChain.cpp: -------------------------------------------------------------------------------- 1 | #include "ResonariumEffectChain.h" 2 | 3 | #include "../ResonatorVoice.h" 4 | #include "../defines.h" 5 | 6 | ResonariumEffectChain::ResonariumEffectChain(EffectChainParams params) 7 | : chorusParams(params.chorusParams), 8 | delay(MAX_DELAY_IN_SECONDS), 9 | delayParams(params.delayParams), 10 | distortion(params.distortionParams), 11 | distortionParams(params.distortionParams), 12 | multiAmp([this]() -> double { return sampleRate; }, params.multiAmpParams), 13 | multiAmpParams(params.multiAmpParams), 14 | phaserParams(params.phaserParams), 15 | compressorParams(params.compressorParams), 16 | reverbParams(params.reverbParams), 17 | filter1(params.filterParams[0]), 18 | filter2(params.filterParams[1]), 19 | filter1Params(params.filterParams[0]), 20 | filter2Params(params.filterParams[1]), 21 | effectChainParams(params) 22 | { 23 | } 24 | 25 | void ResonariumEffectChain::prepare(const juce::dsp::ProcessSpec& spec) 26 | { 27 | chorus.prepare(spec); 28 | delay.prepare(spec); 29 | distortion.prepare(spec); 30 | filter1.prepare(spec); 31 | filter2.prepare(spec); 32 | phaser.prepare(spec); 33 | compressor.prepare(spec); 34 | gain.prepare(spec); 35 | filter1.prepare(spec); 36 | filter2.prepare(spec); 37 | this->sampleRate = spec.sampleRate; 38 | } 39 | 40 | void ResonariumEffectChain::reset() 41 | { 42 | chorus.reset(); 43 | delay.reset(); 44 | filter1.reset(); 45 | filter2.reset(); 46 | phaser.reset(); 47 | mverb.reset(); 48 | gain.reset(); 49 | distortion.reset(); 50 | multiAmp.reset(); 51 | filter1.reset(); 52 | filter2.reset(); 53 | } 54 | 55 | template 56 | void ResonariumEffectChain::updateParameters(T& source, juce::AudioPlayHead* playhead) 57 | { 58 | chorus.setRate(source.getValue(chorusParams.rate, channel)); 59 | chorus.setDepth(source.getValue(chorusParams.depth, channel)); 60 | chorus.setFeedback(source.getValue(chorusParams.feedback, channel)); 61 | chorus.setCentreDelay(source.getValue(chorusParams.delay, channel)); 62 | chorus.setMix(source.getValue(chorusParams.mix, channel)); 63 | 64 | phaser.setRate(source.getValue(phaserParams.rate, channel)); 65 | phaser.setDepth(source.getValue(phaserParams.depth, channel)); 66 | phaser.setFeedback(source.getValue(phaserParams.feedback, channel)); 67 | phaser.setCentreFrequency(source.getValue(phaserParams.centreFrequency, channel)); 68 | phaser.setMix(source.getValue(phaserParams.mix, channel)); 69 | 70 | const float mverbDampingFreq = source.getValue(reverbParams.dampingFreq); 71 | const float mverbDensity = source.getValue(reverbParams.density); 72 | const float mverbBandwidthFreq = source.getValue(reverbParams.bandwidthFreq); 73 | const float mverbDecay = source.getValue(reverbParams.decay); 74 | const float mverbPredelay = source.getValue(reverbParams.predelay); 75 | const float mverbRoomSize = source.getValue(reverbParams.size); 76 | const float mverbMix = source.getValue(reverbParams.mix); 77 | const float mverbEarlyMix = source.getValue(reverbParams.earlyMix); 78 | 79 | jassert(mverbDampingFreq >= 0 && mverbDampingFreq <= 1); 80 | jassert(mverbDensity >= 0 && mverbDensity <= 1); 81 | jassert(mverbBandwidthFreq >= 0 && mverbBandwidthFreq <= 1); 82 | jassert(mverbDecay >= 0 && mverbDecay <= 1); 83 | jassert(mverbPredelay >= 0 && mverbPredelay <= 1); 84 | jassert(mverbRoomSize >= 0 && mverbRoomSize <= 1); 85 | jassert(mverbMix >= 0 && mverbMix <= 1); 86 | jassert(mverbEarlyMix >= 0 && mverbEarlyMix <= 1); 87 | 88 | mverb.setParameter(MVerb::DAMPINGFREQ, mverbDampingFreq); 89 | mverb.setParameter(MVerb::DENSITY, mverbDensity); 90 | mverb.setParameter(MVerb::BANDWIDTHFREQ, mverbBandwidthFreq); 91 | mverb.setParameter(MVerb::DECAY, mverbDecay); 92 | mverb.setParameter(MVerb::PREDELAY, mverbPredelay); 93 | mverb.setParameter(MVerb::SIZE, mverbRoomSize); 94 | mverb.setParameter(MVerb::GAIN, 1); 95 | mverb.setParameter(MVerb::MIX, mverbMix); 96 | mverb.setParameter(MVerb::EARLYMIX, mverbEarlyMix); 97 | 98 | float delayTimeL = 0; 99 | float delayTimeR = 0; 100 | if (delayParams.syncL->getProcValue() > 0.0f) 101 | { 102 | delayTimeL = gin::NoteDuration::getNoteDurations()[size_t(delayParams.beatL->getProcValue())]. 103 | toSeconds(playhead); 104 | } 105 | else 106 | { 107 | delayTimeL = source.getValue(delayParams.timeL, 0); 108 | } 109 | 110 | delay.setDelayTime(0, delayTimeL); 111 | 112 | if (delayParams.lock->isOn()) 113 | { 114 | delayTimeR = delayTimeL; 115 | } 116 | else 117 | { 118 | if (delayParams.syncR->getProcValue() > 0.0f) 119 | { 120 | delayTimeR = gin::NoteDuration::getNoteDurations()[size_t(delayParams.beatR->getProcValue())]. 121 | toSeconds(playhead); 122 | } 123 | else 124 | { 125 | delayTimeR = source.getValue(delayParams.timeR, 1); 126 | } 127 | } 128 | 129 | delay.setDelayTime(1, delayTimeR); 130 | delay.setFeedback(0, source.getValue(delayParams.feedback, 0)); 131 | delay.setFeedback(1, source.getValue(delayParams.feedback, 1)); 132 | delay.setPingPong(delayParams.pingPongAmount->isOn()); 133 | delay.setMix(0, source.getValue(delayParams.mix, 0)); 134 | delay.setMix(1, source.getValue(delayParams.mix, 1)); 135 | 136 | compressor.setThreshold(source.getValue(compressorParams.threshold)); 137 | compressor.setRatio(source.getValue(compressorParams.ratio)); 138 | compressor.setAttack(source.getValue(compressorParams.attack)); 139 | compressor.setRelease(source.getValue(compressorParams.release)); 140 | 141 | distortion.updateParameters(source); 142 | 143 | multiAmp.updateParameters(source); 144 | 145 | filter1.updateParameters(source); 146 | 147 | filter2.updateParameters(source); 148 | 149 | } 150 | 151 | void ResonariumEffectChain::process(juce::dsp::AudioBlock block) noexcept 152 | { 153 | const juce::dsp::ProcessContextReplacing context(block); 154 | if (filter1Params.enabled->isOn()) filter1.process(context); 155 | if (chorusParams.enabled->isOn()) chorus.process(context); 156 | if (phaserParams.enabled->isOn()) phaser.process(context); 157 | if (distortionParams.enabled->isOn()) distortion.process(context); 158 | if (multiAmpParams.enabled->isOn()) multiAmp.process(context); 159 | if (delayParams.enabled->isOn()) delay.process(context); 160 | if (compressorParams.enabled->isOn()) compressor.process(context); 161 | if (reverbParams.enabled->isOn()) 162 | { 163 | float* data[2] = {block.getChannelPointer(0), block.getChannelPointer(1)}; 164 | mverb.process(data, data, block.getNumSamples()); 165 | } 166 | if (filter2Params.enabled->isOn()) filter2.process(context); 167 | } 168 | 169 | 170 | template void ResonariumEffectChain::updateParameters(gin::ModVoice&, juce::AudioPlayHead*); 171 | template void ResonariumEffectChain::updateParameters(gin::ModMatrix&, juce::AudioPlayHead*); 172 | template void ResonariumEffectChain::updateParameters(ResonatorVoice&, juce::AudioPlayHead*); 173 | -------------------------------------------------------------------------------- /plugin/Source/ui/AnimatedScrollBarsViewport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // No forward declarations needed for simple string-based detection 7 | 8 | /** 9 | A custom Viewport that shows its scrollbars on top of the content component 10 | and hides them when no user interactions is performed. 11 | */ 12 | class AnimatedScrollBarsViewport : public juce::Viewport 13 | { 14 | public: 15 | AnimatedScrollBarsViewport(const juce::String& componentName = juce::String()) 16 | : juce::Viewport(componentName) 17 | { 18 | setScrollBarsShown(false, false, true, true); 19 | 20 | scrollBarListener.updateFunction = [this](juce::ScrollBar* bar, double newRangeStart) 21 | { 22 | auto newRangeStartInt = juce::roundToInt(newRangeStart); 23 | 24 | if (bar == &hBar) 25 | { 26 | setViewPosition(newRangeStartInt, getViewPositionY()); 27 | } 28 | else if (bar == &vBar) 29 | { 30 | setViewPosition(getViewPositionX(), newRangeStartInt); 31 | } 32 | }; 33 | 34 | for (auto bar : {&hBar, &vBar}) 35 | { 36 | addChildComponent(bar); 37 | bar->addListener(&scrollBarListener); 38 | } 39 | 40 | addMouseListener(this, true); 41 | } 42 | 43 | void resized() override 44 | { 45 | juce::Viewport::resized(); 46 | updateBars(); 47 | } 48 | 49 | void mouseEnter(const juce::MouseEvent& e) override 50 | { 51 | if (!isMouseOver) 52 | { 53 | isMouseOver = true; 54 | for (auto bar : {&hBar, &vBar}) 55 | { 56 | animator.fadeIn(bar, 400); 57 | bar->setVisible(true); 58 | } 59 | } 60 | } 61 | 62 | void mouseExit(const juce::MouseEvent& e) override 63 | { 64 | //check if mouse is outside the viewport bounds 65 | if (!getLocalBounds().contains(e.getEventRelativeTo(this).getPosition())) 66 | { 67 | isMouseOver = false; 68 | for (auto bar : {&hBar, &vBar}) 69 | { 70 | animator.fadeOut(bar, 400); 71 | } 72 | } 73 | } 74 | 75 | void visibleAreaChanged(const juce::Rectangle& newVisibleArea) override 76 | { 77 | updateBars(); 78 | } 79 | 80 | void mouseWheelMove(const juce::MouseEvent& e, const juce::MouseWheelDetails& wheel) override 81 | { 82 | // Recursion guard to prevent infinite loops 83 | if (isHandlingWheelEvent) 84 | return; 85 | 86 | // If Alt is pressed, try to find a knob under the mouse and delegate to it 87 | if (e.mods.isAltDown()) 88 | { 89 | if (handleKnobWheelEvent(e, wheel)) 90 | return; // Event was handled by a knob 91 | } 92 | 93 | // Otherwise, use normal viewport scrolling 94 | juce::Viewport::mouseWheelMove(e, wheel); 95 | } 96 | 97 | private: 98 | /** 99 | The Viewport is already a ScrollBar::Listener, 100 | this subclass is a workaround so we can 101 | listen also to our custom scrollbars changes. 102 | */ 103 | class ScrollBarListenerImpl : public juce::ScrollBar::Listener 104 | { 105 | public: 106 | std::function updateFunction; 107 | 108 | void scrollBarMoved(juce::ScrollBar* bar, double newRangeStart) override 109 | { 110 | if (updateFunction) 111 | { 112 | updateFunction(bar, newRangeStart); 113 | } 114 | } 115 | }; 116 | 117 | void updateBars() 118 | { 119 | auto viewportBounds = getBounds(); 120 | auto scrollbarWidth = getScrollBarThickness(); 121 | auto contentBounds = getViewedComponent()->getBounds(); 122 | auto singleStepSize = 16; 123 | 124 | hBar.setBounds(0, viewportBounds.getHeight() - scrollbarWidth, viewportBounds.getWidth(), scrollbarWidth); 125 | hBar.setRangeLimits(0.0, contentBounds.getWidth()); 126 | hBar.setCurrentRange(getViewPositionX(), viewportBounds.getWidth()); 127 | hBar.setSingleStepSize(singleStepSize); 128 | hBar.cancelPendingUpdate(); 129 | 130 | vBar.setBounds(viewportBounds.getWidth() - scrollbarWidth, 0, scrollbarWidth, viewportBounds.getHeight()); 131 | vBar.setRangeLimits(0.0, contentBounds.getHeight()); 132 | vBar.setCurrentRange(getViewPositionY(), viewportBounds.getHeight()); 133 | vBar.setSingleStepSize(singleStepSize); 134 | vBar.cancelPendingUpdate(); 135 | } 136 | 137 | bool handleKnobWheelEvent(const juce::MouseEvent& e, const juce::MouseWheelDetails& wheel) 138 | { 139 | // Get the position relative to the viewport's content 140 | auto contentComponent = getViewedComponent(); 141 | if (!contentComponent) 142 | return false; 143 | 144 | // Convert mouse position to content coordinates 145 | auto mousePositionInContent = e.getEventRelativeTo(contentComponent).getPosition(); 146 | 147 | // Find the deepest component at this position 148 | auto* targetComponent = contentComponent->getComponentAt(mousePositionInContent); 149 | if (!targetComponent) 150 | return false; 151 | 152 | // Check if the target component is a knob or contains a knob 153 | auto* knobComponent = findKnobComponent(targetComponent); 154 | if (!knobComponent) 155 | return false; 156 | 157 | // Create a new mouse event relative to the knob component 158 | auto relativeEvent = e.getEventRelativeTo(knobComponent); 159 | 160 | // Set recursion guard before delegating 161 | isHandlingWheelEvent = true; 162 | 163 | // Delegate the wheel event to the knob 164 | knobComponent->mouseWheelMove(relativeEvent, wheel); 165 | 166 | // Clear recursion guard after delegating 167 | isHandlingWheelEvent = false; 168 | 169 | return true; 170 | } 171 | 172 | juce::Component* findKnobComponent(juce::Component* component) 173 | { 174 | if (!component) 175 | return nullptr; 176 | 177 | // Check if the current component is a knob 178 | if (isKnobComponent(component)) 179 | return component; 180 | 181 | // Search up the parent hierarchy to find a knob 182 | for (auto* parent = component->getParentComponent(); parent != nullptr; parent = parent->getParentComponent()) 183 | { 184 | if (isKnobComponent(parent)) 185 | return parent; 186 | 187 | // Don't search beyond the viewport's content 188 | if (parent == getViewedComponent()) 189 | break; 190 | } 191 | 192 | return nullptr; 193 | } 194 | 195 | bool isKnobComponent(juce::Component* component); 196 | 197 | juce::ScrollBar hBar{false}; 198 | juce::ScrollBar vBar{true}; 199 | ScrollBarListenerImpl scrollBarListener; 200 | juce::ComponentAnimator animator; 201 | bool isMouseOver = false; 202 | bool isHandlingWheelEvent = false; 203 | }; 204 | -------------------------------------------------------------------------------- /plugin/Resources/Presets_old/Raindrops from the Other Side.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 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 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /plugin/Source/dsp/airwindows/BigAmp.cpp: -------------------------------------------------------------------------------- 1 | /* ======================================== 2 | * BigAmp - BigAmp.h 3 | * Copyright (c) 2016 airwindows, Airwindows uses the MIT license 4 | * ======================================== */ 5 | 6 | #ifndef __Gain_H 7 | #include "BigAmp.h" 8 | #include 9 | #endif 10 | 11 | JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-conversion", "-Wimplicit-float-conversion", "-Wunused-parameter") 12 | 13 | BigAmp::BigAmp(audioMasterCallback audioMaster) : 14 | AudioEffectX(audioMaster, kNumPrograms, kNumParameters) 15 | { 16 | reset(); 17 | //this is reset: values being initialized only once. Startup values, whatever they are. 18 | 19 | _canDo.insert("plugAsChannelInsert"); // plug-in can be used as a channel insert effect. 20 | _canDo.insert("plugAsSend"); // plug-in can be used as a send effect. 21 | _canDo.insert("x2in2out"); 22 | setNumInputs(kNumInputs); 23 | setNumOutputs(kNumOutputs); 24 | setUniqueID(kUniqueId); 25 | canProcessReplacing(); // supports output replacing 26 | canDoubleReplacing(); // supports double precision processing 27 | programsAreChunks(true); 28 | vst_strncpy (_programName, "Default", kVstMaxProgNameLen); // default program name 29 | } 30 | 31 | BigAmp::~BigAmp() {} 32 | 33 | void BigAmp::reset() 34 | { 35 | A = 0.5; 36 | B = 0.5; 37 | C = 0.8; 38 | D = 1.0; 39 | 40 | lastSampleL = 0.0; 41 | storeSampleL = 0.0; 42 | lastSlewL = 0.0; 43 | iirSampleAL = 0.0; 44 | iirSampleBL = 0.0; 45 | iirSampleCL = 0.0; 46 | iirSampleDL = 0.0; 47 | iirSampleEL = 0.0; 48 | iirSampleFL = 0.0; 49 | iirSampleGL = 0.0; 50 | iirSampleHL = 0.0; 51 | iirSampleIL = 0.0; 52 | iirSampleJL = 0.0; 53 | 54 | lastSampleR = 0.0; 55 | storeSampleR = 0.0; 56 | lastSlewR = 0.0; 57 | iirSampleAR = 0.0; 58 | iirSampleBR = 0.0; 59 | iirSampleCR = 0.0; 60 | iirSampleDR = 0.0; 61 | iirSampleER = 0.0; 62 | iirSampleFR = 0.0; 63 | iirSampleGR = 0.0; 64 | iirSampleHR = 0.0; 65 | iirSampleIR = 0.0; 66 | iirSampleJR = 0.0; 67 | 68 | for (int fcount = 0; fcount < 257; fcount++) { 69 | OddL[fcount] = 0.0; 70 | EvenL[fcount] = 0.0; 71 | OddR[fcount] = 0.0; 72 | EvenR[fcount] = 0.0; 73 | } 74 | 75 | count = 0; 76 | flip = false; //amp 77 | 78 | for(int fcount = 0; fcount < 90; fcount++) { 79 | bL[fcount] = 0; 80 | bR[fcount] = 0; 81 | } 82 | smoothCabAL = 0.0; smoothCabBL = 0.0; lastCabSampleL = 0.0; //cab 83 | smoothCabAR = 0.0; smoothCabBR = 0.0; lastCabSampleR = 0.0; //cab 84 | 85 | for (int fcount = 0; fcount < 9; fcount++) { 86 | lastRefL[fcount] = 0.0; 87 | lastRefR[fcount] = 0.0; 88 | } 89 | cycle = 0; //undersampling 90 | 91 | for (int x = 0; x < fix_total; x++) { 92 | fixA[x] = 0.0; 93 | fixB[x] = 0.0; 94 | fixC[x] = 0.0; 95 | fixD[x] = 0.0; 96 | fixE[x] = 0.0; 97 | fixF[x] = 0.0; 98 | } //filtering 99 | 100 | fpdL = 1.0; while (fpdL < 16386) fpdL = rand()*UINT32_MAX; 101 | fpdR = 1.0; while (fpdR < 16386) fpdR = rand()*UINT32_MAX; 102 | } 103 | 104 | VstInt32 BigAmp::getVendorVersion () {return 1000;} 105 | void BigAmp::setProgramName(char *name) {vst_strncpy (_programName, name, kVstMaxProgNameLen);} 106 | void BigAmp::getProgramName(char *name) {vst_strncpy (name, _programName, kVstMaxProgNameLen);} 107 | //airwindows likes to ignore this stuff. Make your own programs, and make a different plugin rather than 108 | //trying to do versioning and preventing people from using older versions. Maybe they like the old one! 109 | 110 | static float pinParameter(float data) 111 | { 112 | if (data < 0.0f) return 0.0f; 113 | if (data > 1.0f) return 1.0f; 114 | return data; 115 | } 116 | 117 | VstInt32 BigAmp::getChunk (void** data, bool isPreset) 118 | { 119 | float *chunkData = (float *)calloc(kNumParameters, sizeof(float)); 120 | chunkData[0] = A; 121 | chunkData[1] = B; 122 | chunkData[2] = C; 123 | chunkData[3] = D; 124 | /* Note: The way this is set up, it will break if you manage to save settings on an Intel 125 | machine and load them on a PPC Mac. However, it's fine if you stick to the machine you 126 | started with. */ 127 | 128 | *data = chunkData; 129 | return kNumParameters * sizeof(float); 130 | } 131 | 132 | VstInt32 BigAmp::setChunk (void* data, VstInt32 byteSize, bool isPreset) 133 | { 134 | float *chunkData = (float *)data; 135 | A = pinParameter(chunkData[0]); 136 | B = pinParameter(chunkData[1]); 137 | C = pinParameter(chunkData[2]); 138 | D = pinParameter(chunkData[3]); 139 | /* We're ignoring byteSize as we found it to be a filthy liar */ 140 | 141 | /* calculate any other fields you need here - you could copy in 142 | code from setParameter() here. */ 143 | return 0; 144 | } 145 | 146 | void BigAmp::setParameter(VstInt32 index, float value) { 147 | switch (index) { 148 | case kParamA: A = value; break; 149 | case kParamB: B = value; break; 150 | case kParamC: C = value; break; 151 | case kParamD: D = value; break; 152 | default: throw; // unknown parameter, shouldn't happen! 153 | } 154 | } 155 | 156 | float BigAmp::getParameter(VstInt32 index) { 157 | switch (index) { 158 | case kParamA: return A; break; 159 | case kParamB: return B; break; 160 | case kParamC: return C; break; 161 | case kParamD: return D; break; 162 | default: break; // unknown parameter, shouldn't happen! 163 | } return 0.0; //we only need to update the relevant name, this is simple to manage 164 | } 165 | 166 | void BigAmp::getParameterName(VstInt32 index, char *text) { 167 | switch (index) { 168 | case kParamA: vst_strncpy (text, "Gain", kVstMaxParamStrLen); break; 169 | case kParamB: vst_strncpy (text, "Tone", kVstMaxParamStrLen); break; 170 | case kParamC: vst_strncpy (text, "Output", kVstMaxParamStrLen); break; 171 | case kParamD: vst_strncpy (text, "Mix", kVstMaxParamStrLen); break; 172 | default: break; // unknown parameter, shouldn't happen! 173 | } //this is our labels for displaying in the VST host 174 | } 175 | 176 | void BigAmp::getParameterDisplay(VstInt32 index, char *text) { 177 | switch (index) { 178 | case kParamA: float2string (A, text, kVstMaxParamStrLen); break; 179 | case kParamB: float2string (B, text, kVstMaxParamStrLen); break; 180 | case kParamC: float2string (C, text, kVstMaxParamStrLen); break; 181 | case kParamD: float2string (D, text, kVstMaxParamStrLen); break; 182 | default: break; // unknown parameter, shouldn't happen! 183 | } //this displays the values and handles 'popups' where it's discrete choices 184 | } 185 | 186 | void BigAmp::getParameterLabel(VstInt32 index, char *text) { 187 | switch (index) { 188 | case kParamA: vst_strncpy (text, "", kVstMaxParamStrLen); break; 189 | case kParamB: vst_strncpy (text, "", kVstMaxParamStrLen); break; 190 | case kParamC: vst_strncpy (text, "", kVstMaxParamStrLen); break; 191 | case kParamD: vst_strncpy (text, "", kVstMaxParamStrLen); break; 192 | default: break; // unknown parameter, shouldn't happen! 193 | } 194 | } 195 | 196 | VstInt32 BigAmp::canDo(char *text) 197 | { return (_canDo.find(text) == _canDo.end()) ? -1: 1; } // 1 = yes, -1 = no, 0 = don't know 198 | 199 | bool BigAmp::getEffectName(char* name) { 200 | vst_strncpy(name, "BigAmp", kVstMaxProductStrLen); return true; 201 | } 202 | 203 | VstPlugCategory BigAmp::getPlugCategory() {return kPlugCategEffect;} 204 | 205 | bool BigAmp::getProductString(char* text) { 206 | vst_strncpy (text, "airwindows BigAmp", kVstMaxProductStrLen); return true; 207 | } 208 | 209 | bool BigAmp::getVendorString(char* text) { 210 | vst_strncpy (text, "airwindows", kVstMaxVendorStrLen); return true; 211 | } 212 | -------------------------------------------------------------------------------- /plugin/Resources/Presets_old/Phastic Square Pluck.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 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 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | --------------------------------------------------------------------------------