├── Source ├── DryWetMix.h ├── cursor.png ├── audio │ ├── NullNoiseSynth.cpp │ ├── Rectifier.h │ ├── MidSide.h │ ├── Bitcrusher.h │ ├── Rectifier.cpp │ ├── PRM.h │ ├── Bitcrusher.cpp │ ├── WHead.h │ ├── LatencyCompensation.h │ ├── ProcessSuspend.h │ ├── Phasor.h │ ├── PRM.cpp │ ├── EnvelopeFollower.h │ ├── AudioUtils.h │ ├── MidSide.cpp │ ├── MIDILearn.h │ ├── AbsorbProcessor.h │ ├── SpectroBeam.h │ ├── WHead.cpp │ ├── ProcessSuspend.cpp │ ├── Meter.h │ ├── Oscillator.h │ ├── Phasor.cpp │ ├── CombFilter.h │ ├── AutoGain.h │ ├── EnvelopeFollower.cpp │ ├── XenManager.h │ ├── DryWetMix.h │ ├── NullNoiseSynth.h │ ├── WaveTable.h │ ├── LatencyCompensation.cpp │ ├── MIDIManager.h │ ├── OverdriveReNEO.h │ ├── WaveTable.cpp │ ├── SpectroBeam.cpp │ ├── MIDILearn.cpp │ ├── MIDIDelay.h │ ├── Oscillator.cpp │ ├── Filter.h │ ├── Oversampling.h │ ├── AbsorbProcessor.cpp │ ├── Oscilloscope.h │ ├── Meter.cpp │ ├── PitchGlitcher.h │ ├── XenManager.cpp │ ├── AutoGain.cpp │ ├── CombFilter.cpp │ └── Manta.h ├── cursorCross.png ├── fonts │ ├── nel19.ttf │ ├── Dosis │ │ ├── static │ │ │ ├── Dosis-Bold.ttf │ │ │ ├── Dosis-Light.ttf │ │ │ ├── Dosis-Medium.ttf │ │ │ ├── Dosis-Regular.ttf │ │ │ ├── Dosis-ExtraBold.ttf │ │ │ ├── Dosis-SemiBold.ttf │ │ │ └── Dosis-ExtraLight.ttf │ │ ├── Dosis-VariableFont_wght.ttf │ │ ├── README.txt │ │ └── OFL.txt │ ├── Lobster │ │ ├── Lobster-Regular.ttf │ │ └── OFL.txt │ └── Ms_Madi │ │ ├── MsMadi-Regular.ttf │ │ └── OFL.txt ├── vst3_logo_small.png ├── welcome.txt ├── gui │ ├── MIDIVoicesComp.h │ ├── Tooltip.h │ ├── MIDICCMonitor.h │ ├── Shader.h │ ├── LogoComp.h │ ├── ButtonParameterRandomizer.h │ ├── Label.h │ ├── SpectroBeamComp.h │ ├── FormulaParser.h │ ├── MIDICCMonitor.cpp │ ├── ContextMenu.h │ ├── KeyboardComp.h │ ├── Events.h │ ├── LowLevel.h │ ├── TextEditor.h │ ├── Shader.cpp │ ├── ToastComponent.h │ ├── MIDIVoicesComp.cpp │ ├── Shared.h │ ├── Events.cpp │ ├── Tooltip.cpp │ ├── GUIParams.h │ ├── HighLevel.h │ ├── FilterResponseGraph.h │ ├── SpectroBeamComp.cpp │ ├── Oscilloscope.h │ ├── Utils.h │ ├── Label.cpp │ ├── Using.h │ ├── menu.xml │ ├── Menu.h │ ├── WaveTableDisplay.h │ ├── EQPad.h │ ├── Comp.h │ ├── Button.h │ ├── Knob.h │ ├── Shared.cpp │ ├── Layout.h │ ├── PatchBrowser.h │ ├── FilterResponseGraph.cpp │ └── GUIParams.cpp ├── arch │ ├── Range.h │ ├── Interpolation.h │ ├── State.h │ ├── Smooth.h │ ├── FormulaParser2.h │ └── Interpolation.cpp ├── Editor.h ├── Processor.h └── info.h ├── .gitignore └── README.md /Source/DryWetMix.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/Builds 2 | **/JuceLibraryCode 3 | **/build -------------------------------------------------------------------------------- /Source/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/cursor.png -------------------------------------------------------------------------------- /Source/audio/NullNoiseSynth.cpp: -------------------------------------------------------------------------------- 1 | #include "NullNoiseSynth.h" 2 | 3 | namespace audio 4 | { 5 | } -------------------------------------------------------------------------------- /Source/cursorCross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/cursorCross.png -------------------------------------------------------------------------------- /Source/fonts/nel19.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/nel19.ttf -------------------------------------------------------------------------------- /Source/vst3_logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/vst3_logo_small.png -------------------------------------------------------------------------------- /Source/fonts/Dosis/static/Dosis-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Dosis/static/Dosis-Bold.ttf -------------------------------------------------------------------------------- /Source/fonts/Lobster/Lobster-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Lobster/Lobster-Regular.ttf -------------------------------------------------------------------------------- /Source/fonts/Ms_Madi/MsMadi-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Ms_Madi/MsMadi-Regular.ttf -------------------------------------------------------------------------------- /Source/fonts/Dosis/static/Dosis-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Dosis/static/Dosis-Light.ttf -------------------------------------------------------------------------------- /Source/fonts/Dosis/static/Dosis-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Dosis/static/Dosis-Medium.ttf -------------------------------------------------------------------------------- /Source/fonts/Dosis/static/Dosis-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Dosis/static/Dosis-Regular.ttf -------------------------------------------------------------------------------- /Source/fonts/Dosis/static/Dosis-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Dosis/static/Dosis-ExtraBold.ttf -------------------------------------------------------------------------------- /Source/fonts/Dosis/static/Dosis-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Dosis/static/Dosis-SemiBold.ttf -------------------------------------------------------------------------------- /Source/fonts/Dosis/Dosis-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Dosis/Dosis-VariableFont_wght.ttf -------------------------------------------------------------------------------- /Source/fonts/Dosis/static/Dosis-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrugalla/Project/HEAD/Source/fonts/Dosis/static/Dosis-ExtraLight.ttf -------------------------------------------------------------------------------- /Source/welcome.txt: -------------------------------------------------------------------------------- 1 | Welcome to THIS PLUGIN! 2 | 3 | This plugin still needs an intro text lol. 4 | Pls inform me if I forgot to write one! 5 | 6 | Hope u enjoy :3 7 | Florian -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | this was the template i conceptualized a few of my plugins with, like: 2 | https://github.com/Mrugalla/Manta 3 | https://github.com/Mrugalla/Absorb 4 | https://github.com/Mrugalla/PitchGlitcher 5 | -------------------------------------------------------------------------------- /Source/audio/Rectifier.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace audio 4 | { 5 | /* smpls, numSamples */ 6 | void rectify(float*, int) noexcept; 7 | 8 | /* samples, numChannels, numSamples */ 9 | void rectify(float**, int, int) noexcept; 10 | } -------------------------------------------------------------------------------- /Source/audio/MidSide.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace audio 4 | { 5 | /*samples, numSamples, chStart*/ 6 | void encodeMS(float* const*, int, int) noexcept; 7 | 8 | /*samples, numSamples, chStart*/ 9 | void decodeMS(float* const*, int, int) noexcept; 10 | } -------------------------------------------------------------------------------- /Source/audio/Bitcrusher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace audio 4 | { 5 | /* samples, numSamples, gain, gainInv */ 6 | void crush(float*, int, float, const float); 7 | 8 | /* samples, numChannels, numSamples, gain */ 9 | void crush(float**, int, int, float); 10 | } -------------------------------------------------------------------------------- /Source/gui/MIDIVoicesComp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Comp.h" 3 | 4 | namespace gui 5 | { 6 | struct MIDIVoicesComp : 7 | public Comp, 8 | public Timer 9 | { 10 | MIDIVoicesComp(Utils&); 11 | 12 | void timerCallback() override; 13 | 14 | void paint(Graphics&) override; 15 | 16 | protected: 17 | const MIDIVoicesArray& voices; 18 | #if PPD_MIDINumVoices > 0 19 | std::array voicesActive; 20 | #endif 21 | }; 22 | } -------------------------------------------------------------------------------- /Source/gui/Tooltip.h: -------------------------------------------------------------------------------- 1 | #include "Label.h" 2 | 3 | namespace gui 4 | { 5 | struct Tooltip : 6 | public Comp 7 | { 8 | Tooltip(Utils&, String&&/*tooltip*/); 9 | 10 | void updateTooltip(const String*); 11 | 12 | protected: 13 | Label buildDateLabel, tooltipLabel; 14 | 15 | void paint(Graphics&) override; 16 | 17 | void resized() override; 18 | 19 | private: 20 | Notify makeNotify(Tooltip*); 21 | 22 | const String BuildDate; 23 | }; 24 | } -------------------------------------------------------------------------------- /Source/audio/Rectifier.cpp: -------------------------------------------------------------------------------- 1 | #include "Rectifier.h" 2 | #include 3 | 4 | namespace audio 5 | { 6 | void rectify(float* samples, int numSamples) noexcept 7 | { 8 | for (auto s = 0; s < numSamples; ++s) 9 | samples[s] = std::sqrt(samples[s] * samples[s]); 10 | } 11 | 12 | void rectify(float** samples, int numChannels, int numSamples) noexcept 13 | { 14 | for (auto ch = 0; ch < numChannels; ++ch) 15 | rectify(samples[ch], numSamples); 16 | } 17 | } -------------------------------------------------------------------------------- /Source/gui/MIDICCMonitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Label.h" 3 | #include "../audio/MIDILearn.h" 4 | 5 | namespace gui 6 | { 7 | struct MIDICCMonitor : 8 | public Comp, 9 | public Timer 10 | { 11 | using Learn = audio::MIDILearn; 12 | 13 | MIDICCMonitor(Utils&, const Learn&); 14 | 15 | protected: 16 | const Learn& learn; 17 | int idx; 18 | Label label; 19 | 20 | void paint(Graphics&); 21 | 22 | void resized() override; 23 | 24 | void timerCallback() override; 25 | 26 | String toString(); 27 | }; 28 | } -------------------------------------------------------------------------------- /Source/audio/PRM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AudioUtils.h" 3 | 4 | namespace audio 5 | { 6 | struct PRM 7 | { 8 | /* startVal */ 9 | PRM(float); 10 | 11 | /* Fs, blockSize, smoothLenMs */ 12 | void prepare(float, int, float); 13 | 14 | /* value, numSamples */ 15 | float* operator()(float, int) noexcept; 16 | 17 | /* numSamples */ 18 | float* operator()(int) noexcept; 19 | 20 | /* idx */ 21 | float operator[](int) const noexcept; 22 | 23 | Smooth smooth; 24 | std::vector buf; 25 | bool smoothing; 26 | }; 27 | } -------------------------------------------------------------------------------- /Source/audio/Bitcrusher.cpp: -------------------------------------------------------------------------------- 1 | #include "Bitcrusher.h" 2 | #include 3 | 4 | namespace audio 5 | { 6 | void crush(float* samples, int numSamples, float gain, 7 | const float gainInv) 8 | { 9 | for (auto s = 0; s < numSamples; ++s) 10 | samples[s] = std::round(samples[s] * gain) * gainInv; 11 | } 12 | 13 | void crush(float** samples, int numChannels, int numSamples, float gain) 14 | { 15 | const auto gainInv = 1.f / gain; 16 | for (auto ch = 0; ch < numChannels; ++ch) 17 | crush(samples[ch], numSamples, gain, gainInv); 18 | } 19 | } -------------------------------------------------------------------------------- /Source/gui/Shader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Utils.h" 3 | #include "Layout.h" 4 | 5 | namespace gui 6 | { 7 | struct Shader : 8 | juce::ImageEffectFilter, 9 | Timer 10 | { 11 | Shader(Utils&, Component&); 12 | 13 | void applyEffect(Image&, Graphics&, float, float) override; 14 | 15 | Component& comp; 16 | Utils& utils; 17 | Evt notify; 18 | bool bypassed; 19 | 20 | void paintBypassed(Image&, Graphics&); 21 | 22 | void timerCallback() override; 23 | }; 24 | } -------------------------------------------------------------------------------- /Source/audio/WHead.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace audio 5 | { 6 | struct WHead 7 | { 8 | WHead(); 9 | 10 | /* blockSize, delaySize */ 11 | void prepare(int, int); 12 | 13 | /* numSamples */ 14 | void operator()(int numSamples) noexcept; 15 | 16 | int operator[](int) const noexcept; 17 | 18 | const int* data() const noexcept; 19 | 20 | int* data() noexcept; 21 | 22 | // shift, numSamples 23 | void shift(int, int) noexcept; 24 | protected: 25 | std::vector buf; 26 | int wHead, delaySize; 27 | }; 28 | } -------------------------------------------------------------------------------- /Source/gui/LogoComp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Comp.h" 3 | 4 | namespace gui 5 | { 6 | struct LogoComp : 7 | public Comp 8 | { 9 | LogoComp(Utils& u, const char* _data, const int _size) : 10 | Comp(u, "logo tooltip", CursorType::Default), 11 | drawable(Drawable::createFromImageData(_data, _size)) 12 | { 13 | addAndMakeVisible(*drawable); 14 | } 15 | 16 | UniqueDrawable drawable; 17 | 18 | void paint(Graphics&) override 19 | { 20 | } 21 | 22 | void resized() override 23 | { 24 | drawable->setBounds(getLocalBounds()); 25 | } 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /Source/audio/LatencyCompensation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AudioUtils.h" 3 | #include "WHead.h" 4 | 5 | namespace audio 6 | { 7 | struct LatencyCompensation 8 | { 9 | LatencyCompensation(); 10 | 11 | /* blockSize, latency */ 12 | void prepare(int, int); 13 | 14 | /* dry, inputSamples, numChannels, numSamples */ 15 | void operator()(float* const*, float* const*, int, int) noexcept; 16 | 17 | /* samples, numChannels, numSamples */ 18 | void operator()(float* const*, int, int) noexcept; 19 | 20 | protected: 21 | AudioBuffer ring; 22 | WHead wHead; 23 | public: 24 | int latency; 25 | }; 26 | } -------------------------------------------------------------------------------- /Source/audio/ProcessSuspend.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AudioUtils.h" 3 | 4 | namespace audio 5 | { 6 | struct ProcessSuspender 7 | { 8 | enum class Stage 9 | { 10 | Running, 11 | Suspending, 12 | Suspended, 13 | NumStages 14 | }; 15 | 16 | ProcessSuspender(juce::AudioProcessor&); 17 | 18 | void suspend() noexcept; 19 | 20 | /* returns true if suspending is needed (= return from processBlock) */ 21 | bool suspendIfNeeded(AudioBuffer&) noexcept; 22 | 23 | void prepareToPlay() noexcept; 24 | 25 | protected: 26 | juce::AudioProcessor& processor; 27 | std::atomic stage; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /Source/audio/Phasor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace audio 4 | { 5 | template 6 | struct PhaseInfo 7 | { 8 | PhaseInfo(Float = static_cast(0), Float = static_cast(0)); 9 | 10 | Float phase; 11 | bool retrig; 12 | }; 13 | 14 | template 15 | struct Phasor 16 | { 17 | using Phase = PhaseInfo; 18 | 19 | void setFrequencyHz(Float) noexcept; 20 | 21 | /* phase, inc */ 22 | Phasor(Float = static_cast(0), Float = static_cast(0)); 23 | 24 | /* fsInv */ 25 | void prepare(Float) noexcept; 26 | 27 | void reset(Float = static_cast(0)) noexcept; 28 | 29 | Phase operator()() noexcept; 30 | 31 | Phase phase; 32 | Float inc, fsInv; 33 | }; 34 | } -------------------------------------------------------------------------------- /Source/audio/PRM.cpp: -------------------------------------------------------------------------------- 1 | #include "PRM.h" 2 | 3 | namespace audio 4 | { 5 | PRM::PRM(float startVal) : 6 | smooth(startVal), 7 | buf(), 8 | smoothing(false) 9 | {} 10 | 11 | void PRM::prepare(float Fs, int blockSize, float smoothLenMs) 12 | { 13 | buf.resize(blockSize); 14 | smooth.makeFromDecayInMs(smoothLenMs, Fs); 15 | } 16 | 17 | float* PRM::operator()(float value, int numSamples) noexcept 18 | { 19 | smoothing = smooth(buf.data(), value, numSamples); 20 | return buf.data(); 21 | } 22 | 23 | float* PRM::operator()(int numSamples) noexcept 24 | { 25 | smoothing = smooth(buf.data(), numSamples); 26 | return buf.data(); 27 | } 28 | 29 | float PRM::operator[](int i) const noexcept 30 | { 31 | return buf[i]; 32 | } 33 | } -------------------------------------------------------------------------------- /Source/audio/EnvelopeFollower.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../arch/Smooth.h" 3 | 4 | namespace audio 5 | { 6 | /* 7 | * expects rectified input signal 8 | */ 9 | struct EnvFol 10 | { 11 | enum class State { Rise, Fall, NumStates }; 12 | 13 | using Smoothie = smooth::Lowpass; 14 | 15 | EnvFol(); 16 | 17 | // Fs 18 | void prepare(float) noexcept; 19 | 20 | /* buf, smpls, numSamples, riseInMs, fallInMs */ 21 | void operator()(float*, const float*, int, 22 | float, float) noexcept; 23 | 24 | /* smpl, riseInMs, fallInMs */ 25 | float process(float, float, float) noexcept; 26 | 27 | protected: 28 | Smoothie smoothie; 29 | float rise, fall, env, Fs; 30 | State state; 31 | 32 | float processRise(float) noexcept; 33 | 34 | float processFall(float) noexcept; 35 | }; 36 | } -------------------------------------------------------------------------------- /Source/gui/ButtonParameterRandomizer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Button.h" 3 | 4 | namespace gui 5 | { 6 | struct ButtonParameterRandomizer : 7 | public Button 8 | { 9 | using RandFunc = std::function; 10 | 11 | ButtonParameterRandomizer(Utils&); 12 | 13 | void add(Param*); 14 | 15 | void add(std::vector&&); 16 | 17 | void add(const std::vector&); 18 | 19 | void add(const RandFunc&); 20 | 21 | /* isAbsolute */ 22 | void operator()(bool); 23 | 24 | std::vector randomizables; 25 | std::vector randFuncs; 26 | 27 | protected: 28 | void mouseUp(const Mouse&) override; 29 | 30 | void mouseExit(const Mouse&) override; 31 | 32 | String makeTooltip(); 33 | }; 34 | } -------------------------------------------------------------------------------- /Source/gui/Label.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Comp.h" 3 | 4 | namespace gui 5 | { 6 | struct Label : 7 | public Comp 8 | { 9 | enum class Mode 10 | { 11 | None, 12 | WindowToTextBounds, 13 | TextToLabelBounds, 14 | NumModes 15 | }; 16 | 17 | /* utils, text, notify */ 18 | Label(Utils&, const String&, Notify && = [](EvtType, const void*) {}); 19 | 20 | void setText(const String&); 21 | 22 | const String& getText() const; 23 | 24 | void setMinFontHeight(float); 25 | 26 | bool empty() const noexcept; 27 | 28 | std::vector group; 29 | ColourID textCID; 30 | Just just; 31 | Font font; 32 | float minFontHeight; 33 | Mode mode; 34 | 35 | void updateTextBounds(); 36 | protected: 37 | String text; 38 | 39 | void paint(Graphics&) override; 40 | 41 | void resized() override; 42 | }; 43 | } -------------------------------------------------------------------------------- /Source/audio/AudioUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../arch/Smooth.h" 3 | #include "../param/Param.h" 4 | #include "../arch/State.h" 5 | #include "../arch/Conversion.h" 6 | 7 | namespace audio 8 | { 9 | using AudioBuffer = juce::AudioBuffer; 10 | using SIMD = juce::FloatVectorOperations; 11 | 12 | using MIDIBuffer = juce::MidiBuffer; 13 | using MIDIMessage = juce::MidiMessage; 14 | using MIDIIt = juce::MidiBufferIterator; 15 | using MIDIRef = MIDIIt::reference; 16 | 17 | using String = juce::String; 18 | using Decibels = juce::Decibels; 19 | using ScopedNoDenormals = juce::ScopedNoDenormals; 20 | using PlayHeadPos = juce::AudioPlayHead::CurrentPositionInfo; 21 | 22 | using Smooth = smooth::Smooth; 23 | using PID = param::PID; 24 | using Params = param::Params; 25 | using Param = param::Param; 26 | using State = sta::State; 27 | } -------------------------------------------------------------------------------- /Source/gui/SpectroBeamComp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Comp.h" 3 | #include "../audio/SpectroBeam.h" 4 | #include "../audio/XenManager.h" 5 | #include "../arch/Interpolation.h" 6 | #include "../arch/Conversion.h" 7 | #include 8 | 9 | namespace gui 10 | { 11 | template 12 | struct SpectroBeamComp : 13 | public Comp, 14 | public Timer 15 | { 16 | using SpecBeam = audio::SpectroBeam; 17 | static constexpr int Size = SpecBeam::Size; 18 | static constexpr float SizeF = static_cast(Size); 19 | static constexpr float SizeInv = 1.f / SizeF; 20 | static constexpr float SizeFHalf = SizeF * .5f; 21 | 22 | SpectroBeamComp(Utils&, SpecBeam&); 23 | 24 | void paint(Graphics&) override; 25 | 26 | void timerCallback() override; 27 | 28 | ColourID mainColCID; 29 | protected: 30 | const audio::XenManager& xen; 31 | SpecBeam& beam; 32 | Image img; 33 | }; 34 | } -------------------------------------------------------------------------------- /Source/arch/Range.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "juce_core/juce_core.h" 3 | 4 | namespace makeRange 5 | { 6 | using Range = juce::NormalisableRange; 7 | using String = juce::String; 8 | 9 | /* start, end, bias[-1, 1] */ 10 | Range biased(float, float, float) noexcept; 11 | 12 | /* start, end, steps = 1 */ 13 | Range stepped(float, float, float = 1.f) noexcept; 14 | 15 | Range toggle() noexcept; 16 | 17 | /* start, end */ 18 | Range lin(float, float) noexcept; 19 | 20 | /* start, end, centre */ 21 | Range withCentre(float, float, float) noexcept; 22 | 23 | /* min, max */ 24 | Range foleysLogRange(float, float) noexcept; 25 | 26 | /* min, max, numSteps ]1, N] */ 27 | Range quad(float, float, int) noexcept; 28 | 29 | /* minDenominator, maxDenominator, withZero 30 | for example { 16, .5, true } 31 | starts at 0, then 1/16 and ends at 2/1 32 | */ 33 | Range beats(float, float, bool = false); 34 | } -------------------------------------------------------------------------------- /Source/arch/Interpolation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | namespace interpolate 8 | { 9 | enum class Type 10 | { 11 | Lerp, 12 | CubicHermiteSpline, 13 | NumTypes 14 | }; 15 | 16 | static constexpr int NumTypes = static_cast(Type::NumTypes); 17 | 18 | /* samples, idx, size */ 19 | template 20 | T lerp(const T*, T, int) noexcept; 21 | 22 | /* samples, idx */ 23 | template 24 | T lerp(const T*, T) noexcept; 25 | 26 | /* samples, idx, size */ 27 | template 28 | T cubicHermiteSpline(const T*, T, int) noexcept; 29 | 30 | /* samples, idx */ 31 | template 32 | T cubicHermiteSpline(const T*, T) noexcept; 33 | 34 | /// 35 | 36 | namespace polynomial 37 | { 38 | template 39 | std::function getFunc(const std::vector>&); 40 | } 41 | } -------------------------------------------------------------------------------- /Source/audio/MidSide.cpp: -------------------------------------------------------------------------------- 1 | #include "MidSide.h" 2 | #include "AudioUtils.h" 3 | 4 | namespace audio 5 | { 6 | void encodeMS(float* const* samples, int numSamples, int ch0) noexcept 7 | { 8 | const auto ch1 = ch0 + 1; 9 | 10 | for (auto s = 0; s < numSamples; ++s) 11 | { 12 | const auto mid = samples[ch0][s] + samples[ch1][s]; 13 | const auto side = samples[ch0][s] - samples[ch1][s]; 14 | 15 | samples[ch0][s] = mid; 16 | samples[ch1][s] = side; 17 | } 18 | 19 | for (auto ch = ch0; ch < ch0 + 2; ++ch) 20 | SIMD::multiply(samples[ch], .5f, numSamples); 21 | } 22 | 23 | void decodeMS(float* const* samples, int numSamples, int ch0) noexcept 24 | { 25 | const auto ch1 = ch0 + 1; 26 | 27 | for (auto s = 0; s < numSamples; ++s) 28 | { 29 | const auto left = samples[ch0][s] + samples[ch1][s]; 30 | const auto right = samples[ch0][s] - samples[ch1][s]; 31 | 32 | samples[ch0][s] = left; 33 | samples[ch1][s] = right; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Source/audio/MIDILearn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "AudioUtils.h" 5 | 6 | namespace audio 7 | { 8 | class MIDILearn 9 | { 10 | static constexpr float ValInv = 1.f / 128.f; 11 | 12 | struct CC 13 | { 14 | CC(); 15 | 16 | void setValue(int); 17 | 18 | std::atomic param; 19 | }; 20 | 21 | public: 22 | MIDILearn(Params&, State&); 23 | 24 | void savePatch() const; 25 | 26 | void loadPatch(); 27 | 28 | void processBlockInit() noexcept; 29 | 30 | void processBlockMIDICC(const MIDIMessage& msg) noexcept; 31 | 32 | void processBlockEnd() noexcept; 33 | 34 | void assignParam(param::Param*) noexcept; 35 | 36 | void removeParam(param::Param*) noexcept; 37 | 38 | std::array ccBuf; 39 | std::atomic ccIdx; 40 | protected: 41 | std::atomic assignableParam; 42 | Params& params; 43 | State& state; 44 | int c; 45 | 46 | String getIDString(int) const; 47 | }; 48 | } -------------------------------------------------------------------------------- /Source/gui/FormulaParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../arch/FormulaParser2.h" 3 | #include "TextEditor.h" 4 | 5 | namespace gui 6 | { 7 | struct FormulaParser : 8 | public TextEditor 9 | { 10 | using Parser = fx::Parser; 11 | 12 | enum PostFX 13 | { 14 | DCOffset, 15 | Windowing, 16 | Normalize, 17 | NumPostFX 18 | }; 19 | 20 | /* utils, tooltip, tables, table size, table overshoot length */ 21 | FormulaParser(Utils&, String&&, std::vector&, int, int = 0); 22 | 23 | /* samples */ 24 | std::array postFX; 25 | Parser fx; 26 | std::function updateFormula; 27 | }; 28 | 29 | struct FormulaParser2 : 30 | public Comp 31 | { 32 | /* utils, tooltip, tables, table size, table overshoot length */ 33 | FormulaParser2(Utils&, String&&, std::vector&, int, int); 34 | 35 | void paint(Graphics&); 36 | 37 | void resized() override; 38 | 39 | FormulaParser parser; 40 | Button dc, normalize, windowing, random, create; 41 | }; 42 | } -------------------------------------------------------------------------------- /Source/gui/MIDICCMonitor.cpp: -------------------------------------------------------------------------------- 1 | #include "MIDICCMonitor.h" 2 | 3 | namespace gui 4 | { 5 | MIDICCMonitor::MIDICCMonitor(Utils& u, const Learn& _learn) : 6 | Comp(u, "Monitors your input controller number for MIDI Learn.", CursorType::Default), 7 | learn(_learn), 8 | idx(learn.ccIdx.load()), 9 | label(u, idx < 0 ? "cc: .." : toString()) 10 | { 11 | addAndMakeVisible(label); 12 | label.textCID = ColourID::Hover; 13 | label.just = Just::left; 14 | startTimerHz(24); 15 | } 16 | 17 | void MIDICCMonitor::paint(Graphics&) 18 | { 19 | 20 | } 21 | 22 | void MIDICCMonitor::resized() 23 | { 24 | label.setBounds(getLocalBounds()); 25 | } 26 | 27 | void MIDICCMonitor::timerCallback() 28 | { 29 | const auto nIdx = learn.ccIdx.load(); 30 | if (nIdx < 0) 31 | return; 32 | 33 | if (idx != nIdx) 34 | { 35 | idx = nIdx; 36 | 37 | label.setText(toString()); 38 | label.repaint(); 39 | } 40 | } 41 | 42 | String MIDICCMonitor::toString() 43 | { 44 | return "cc: " + String(idx); 45 | } 46 | } -------------------------------------------------------------------------------- /Source/audio/AbsorbProcessor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AudioUtils.h" 3 | #include "PRM.h" 4 | 5 | namespace audio 6 | { 7 | class AbsorbProcessor 8 | { 9 | struct Textures 10 | { 11 | Textures(); 12 | 13 | void prepare(float, int); 14 | 15 | /* 16 | samples, numChannels, numSamples, samplesSC, numChannelsSC, rm, am, shapr, crushr, foldr 17 | */ 18 | void operator()(float**, int, int, float**, int, float, float, float, float, float) noexcept; 19 | 20 | protected: 21 | PRM rm, am, shapr, crushr, foldr; 22 | }; 23 | 24 | public: 25 | AbsorbProcessor(); 26 | 27 | void prepare(float, int); 28 | 29 | /* 30 | samples, numChannels, numSamples, samplesSC, numChannelsSC, rm, am, shapr, crushr, foldr 31 | */ 32 | void operator()(float**, int, int, float**, int, float, float, float, float, float) noexcept; 33 | 34 | protected: 35 | Textures textures; 36 | }; 37 | 38 | } -------------------------------------------------------------------------------- /Source/audio/SpectroBeam.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AudioUtils.h" 3 | #include 4 | 5 | namespace audio 6 | { 7 | template 8 | struct SpectroBeam 9 | { 10 | static constexpr size_t Size = 1 << Order; 11 | static constexpr size_t Size2 = Size * 2; 12 | static constexpr size_t SizeHalf = Size / 2; 13 | static constexpr float SizeF = static_cast(Size); 14 | static constexpr float SizeInv = 1.f / SizeF; 15 | 16 | using FFT = juce::dsp::FFT; 17 | using Fifo = std::array; 18 | using Fifo2 = std::array; 19 | 20 | SpectroBeam(); 21 | 22 | /* blockSize */ 23 | void prepare(int); 24 | 25 | /* samples, numChannels, numSamples */ 26 | void operator()(float**, int, int) noexcept; 27 | 28 | /* numSamples */ 29 | void process(int) noexcept; 30 | 31 | protected: 32 | std::vector smpls; 33 | FFT fft; 34 | Fifo2 fifo; 35 | Fifo window; 36 | public: 37 | Fifo2 buffer; 38 | std::atomic ready; 39 | protected: 40 | int idx; 41 | }; 42 | } -------------------------------------------------------------------------------- /Source/gui/ContextMenu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Button.h" 3 | #include "TextEditor.h" 4 | 5 | namespace gui 6 | { 7 | class ContextMenu : 8 | public CompWidgetable 9 | { 10 | Notify makeNotify(ContextMenu&); 11 | 12 | public: 13 | ContextMenu(Utils&); 14 | 15 | void init(); 16 | 17 | void place(const Comp*); 18 | 19 | void paint(Graphics&) override; 20 | 21 | /* name, tooltip */ 22 | void addButton(String&&, String&&); 23 | 24 | /* onClick, idx */ 25 | void setButton(const Button::OnClick&, int); 26 | 27 | std::vector> buttons; 28 | std::vector labelPtr; 29 | protected: 30 | PointF origin; 31 | BoundsF bounds; 32 | 33 | void resized() override; 34 | }; 35 | 36 | 37 | class ContextMenuButtons : 38 | public ContextMenu 39 | { 40 | Notify makeNotify2(ContextMenuButtons&); 41 | 42 | public: 43 | ContextMenuButtons(Utils&); 44 | }; 45 | 46 | 47 | class ContextMenuMacro : 48 | public ContextMenu 49 | { 50 | Notify makeNotify2(ContextMenuMacro&); 51 | 52 | public: 53 | ContextMenuMacro(Utils&); 54 | }; 55 | } -------------------------------------------------------------------------------- /Source/audio/WHead.cpp: -------------------------------------------------------------------------------- 1 | #include "WHead.h" 2 | 3 | namespace audio 4 | { 5 | 6 | WHead::WHead() : 7 | buf(), 8 | wHead(0), 9 | delaySize(1) 10 | {} 11 | 12 | void WHead::prepare(int blockSize, int _delaySize) 13 | { 14 | delaySize = _delaySize; 15 | if (delaySize != 0) 16 | { 17 | wHead = wHead % delaySize; 18 | buf.resize(blockSize); 19 | } 20 | } 21 | 22 | void WHead::operator()(int numSamples) noexcept 23 | { 24 | for (auto s = 0; s < numSamples; ++s, wHead = (wHead + 1) % delaySize) 25 | buf[s] = wHead; 26 | } 27 | 28 | int WHead::operator[](int i) const noexcept 29 | { 30 | return buf[i]; 31 | } 32 | 33 | const int* WHead::data() const noexcept 34 | { 35 | return buf.data(); 36 | } 37 | 38 | int* WHead::data() noexcept 39 | { 40 | return buf.data(); 41 | } 42 | 43 | void WHead::shift(int shift, int numSamples) noexcept 44 | { 45 | for (auto s = 0; s < numSamples; ++s) 46 | { 47 | buf[s] = buf[s] + shift; 48 | if (buf[s] > delaySize) 49 | buf[s] -= delaySize; 50 | else if (buf[s] < 0) 51 | buf[s] += delaySize; 52 | } 53 | wHead = buf[numSamples - 1]; 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /Source/audio/ProcessSuspend.cpp: -------------------------------------------------------------------------------- 1 | #include "ProcessSuspend.h" 2 | 3 | namespace audio 4 | { 5 | ProcessSuspender::ProcessSuspender(juce::AudioProcessor& p) : 6 | processor(p), 7 | stage(Stage::Running) 8 | { 9 | 10 | } 11 | 12 | void ProcessSuspender::suspend() noexcept 13 | { 14 | stage.store(Stage::Suspending); 15 | } 16 | 17 | bool ProcessSuspender::suspendIfNeeded(AudioBuffer& buf) noexcept 18 | { 19 | auto samples = buf.getArrayOfWritePointers(); 20 | const auto numChannels = buf.getNumChannels(); 21 | const auto numSamples = buf.getNumSamples(); 22 | 23 | const auto stg = stage.load(); 24 | if (stg == Stage::Running) 25 | return false; 26 | 27 | if (numSamples != 0) 28 | for (auto ch = 0; ch < numChannels; ++ch) 29 | SIMD::fill(samples[ch], 0.f, numSamples); 30 | 31 | if (stg == Stage::Suspending) 32 | { 33 | stage.store(Stage::Suspended); 34 | processor.prepareToPlay 35 | ( 36 | processor.getSampleRate(), 37 | processor.getBlockSize() 38 | ); 39 | } 40 | 41 | return true; 42 | } 43 | 44 | void ProcessSuspender::prepareToPlay() noexcept 45 | { 46 | stage.store(Stage::Running); 47 | } 48 | } -------------------------------------------------------------------------------- /Source/audio/Meter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "WHead.h" 3 | #include "EnvelopeFollower.h" 4 | #include 5 | #include 6 | 7 | namespace audio 8 | { 9 | class Meters 10 | { 11 | static constexpr float RiseInMs = .01f, FallInMs = 42.f; 12 | 13 | struct Val 14 | { 15 | Val(); 16 | 17 | float rect, val; 18 | std::atomic env; 19 | EnvFol envFol; 20 | }; 21 | 22 | public: 23 | enum Type 24 | { 25 | #if PPDHasGainIn 26 | In, 27 | #endif 28 | Out, 29 | NumTypes 30 | }; 31 | 32 | Meters(); 33 | 34 | /*sampleRate, blockSize*/ 35 | void prepare(float, int); 36 | 37 | #if PPDHasGainIn 38 | /*samples,numChannels,numSamples*/ 39 | void processIn(const float* const*, int, int) noexcept; 40 | #endif 41 | /*samples,numChannels,numSamples*/ 42 | void processOut(const float* const*, int, int) noexcept; 43 | 44 | const std::atomic& operator()(int i) const noexcept; 45 | 46 | protected: 47 | std::array vals; 48 | WHead wHead; 49 | float lenInv; 50 | int length; 51 | 52 | private: 53 | /*val,samples,numChannels,numSamples*/ 54 | void process(Val&, const float* const*, int, int) noexcept; 55 | }; 56 | } -------------------------------------------------------------------------------- /Source/audio/Oscillator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../arch/Smooth.h" 3 | #include 4 | #include "Phasor.h" 5 | #include 6 | 7 | namespace audio 8 | { 9 | using AudioBuffer = juce::AudioBuffer; 10 | 11 | template 12 | struct OscSine 13 | { 14 | OscSine(); 15 | 16 | /* fsInv */ 17 | void prepare(Float); 18 | 19 | void setFreqHz(Float); 20 | 21 | void reset(Float = static_cast(0)); 22 | 23 | /* buffer, numSamples */ 24 | Float* operator()(Float*, int) noexcept; 25 | 26 | Float operator()() noexcept; 27 | 28 | Phasor phasor; 29 | protected: 30 | Float synthesizeSample(); 31 | }; 32 | 33 | template 34 | struct RingModSimple 35 | { 36 | using Osc = OscSine; 37 | using Smooth = smooth::Smooth; 38 | 39 | RingModSimple(); 40 | 41 | /* Fs, blockSize */ 42 | void prepare(Float, int); 43 | 44 | /* samples, numChannels, numSamples, freq */ 45 | void operator()(Float**, int, int, Float**) noexcept; 46 | 47 | protected: 48 | std::array osc; 49 | juce::AudioBuffer freqBuffer; 50 | std::array freqSmooth; 51 | }; 52 | } -------------------------------------------------------------------------------- /Source/gui/KeyboardComp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Button.h" 3 | #include "../arch/Conversion.h" 4 | 5 | namespace gui 6 | { 7 | struct KeyboardComp : 8 | public Comp 9 | { 10 | /* noteVal */ 11 | using PitchCallback = std::function; 12 | 13 | protected: 14 | struct Key : 15 | public Comp 16 | { 17 | /* utils, noteVal */ 18 | Key(Utils&, int); 19 | 20 | void paint(Graphics&) override; 21 | 22 | void resized() override; 23 | 24 | int noteVal; 25 | protected: 26 | Colour bgCol; 27 | Label label; 28 | }; 29 | 30 | public: 31 | /* utils, tooltip */ 32 | KeyboardComp(Utils&, String&&); 33 | 34 | void resized() override; 35 | 36 | void paint(Graphics&) override; 37 | 38 | void mouseMove(const Mouse&) override; 39 | 40 | void mouseDown(const Mouse&) override; 41 | 42 | void mouseDrag(const Mouse&) override; 43 | 44 | void mouseUp(const Mouse&) override; 45 | 46 | void mouseExit(const Mouse&) override; 47 | 48 | PitchCallback onDown, onDrag, onUp; 49 | protected: 50 | std::array, 24> keys; 51 | Button octDown, octUp; 52 | int hoverIdx, octIdx; 53 | 54 | int getHoverIdx(Point) noexcept; 55 | 56 | void keyRangeChanged(); 57 | 58 | int getPitch() noexcept; 59 | }; 60 | 61 | } -------------------------------------------------------------------------------- /Source/gui/Events.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace evt 6 | { 7 | enum class Type 8 | { 9 | ColourSchemeChanged, 10 | TooltipUpdated, 11 | ButtonClicked, 12 | ButtonRightClicked, 13 | ParametrRightClicked, 14 | ParametrDragged, 15 | ClickedEmpty, 16 | PatchUpdated, 17 | ComponentAdded, 18 | EnterParametrValue, 19 | BrowserOpened, 20 | BrowserClosed, 21 | FormulaUpdated, 22 | Toast, 23 | NumTypes 24 | }; 25 | 26 | using Notify = std::function; 27 | 28 | struct System 29 | { 30 | struct Evt 31 | { 32 | Evt(System&); 33 | 34 | Evt(System&, const Notify&); 35 | 36 | Evt(System&, Notify&&); 37 | 38 | Evt(const Evt&); 39 | 40 | ~Evt(); 41 | 42 | void operator()(const Type, const void* = nullptr) const; 43 | 44 | Notify notifier; 45 | protected: 46 | System& sys; 47 | }; 48 | 49 | System(); 50 | 51 | void notify(const Type, const void* = nullptr); 52 | 53 | protected: 54 | std::vector evts; 55 | 56 | void add(Evt*); 57 | 58 | void remove(const Evt*); 59 | }; 60 | } -------------------------------------------------------------------------------- /Source/audio/Phasor.cpp: -------------------------------------------------------------------------------- 1 | #include "Phasor.h" 2 | 3 | namespace audio 4 | { 5 | // PhasorInfo 6 | 7 | template 8 | PhaseInfo::PhaseInfo(Float _phase, Float _retrig) : 9 | phase(_phase), 10 | retrig(_retrig) 11 | {} 12 | 13 | template struct PhaseInfo; 14 | template struct PhaseInfo; 15 | 16 | // Phasor 17 | 18 | template 19 | void Phasor::setFrequencyHz(Float hz) noexcept 20 | { 21 | inc = hz * fsInv; 22 | } 23 | 24 | template 25 | Phasor::Phasor(Float _phase, Float _inc) : 26 | phase(_phase, false), 27 | inc(_inc), 28 | fsInv(static_cast(1)) 29 | { 30 | 31 | } 32 | 33 | template 34 | void Phasor::prepare(Float _fsInv) noexcept 35 | { 36 | fsInv = _fsInv; 37 | } 38 | 39 | template 40 | void Phasor::reset(Float p) noexcept 41 | { 42 | phase.phase = p; 43 | } 44 | 45 | template 46 | Phasor::Phase Phasor::operator()() noexcept 47 | { 48 | phase.phase += inc; 49 | if (phase.phase >= static_cast(1)) 50 | { 51 | --phase.phase; 52 | phase.retrig = true; 53 | return phase; 54 | } 55 | phase.retrig = false; 56 | return phase; 57 | } 58 | 59 | template struct Phasor; 60 | template struct Phasor; 61 | } -------------------------------------------------------------------------------- /Source/gui/LowLevel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Knob.h" 3 | #include "EnvelopeGenerator.h" 4 | 5 | namespace gui 6 | { 7 | struct LowLevel : 8 | public Comp, 9 | public Timer 10 | { 11 | LowLevel(Utils& u) : 12 | Comp(u, "", CursorType::Default), 13 | Timer(), 14 | cutoff(u), 15 | q(u), 16 | quality(u) 17 | { 18 | makeParameter(cutoff, PID::FilterCutoff, "Cutoff"); 19 | addAndMakeVisible(cutoff); 20 | 21 | makeParameter(q, PID::FilterQ, "Q"); 22 | addAndMakeVisible(q); 23 | 24 | makeParameter(quality, PID::FilterSmoothUpsampler, "Quality"); 25 | addAndMakeVisible(quality); 26 | 27 | layout.init 28 | ( 29 | { 1, 13, 13, 13, 1 }, 30 | { 1, 2, 1 } 31 | ); 32 | } 33 | 34 | void paint(Graphics&) override 35 | { 36 | } 37 | 38 | void resized() override 39 | { 40 | layout.resized(); 41 | 42 | layout.place(cutoff, 1, 1, 1, 1, false); 43 | layout.place(q, 2, 1, 1, 1, false); 44 | layout.place(quality, 3, 1, 1, 1, false); 45 | } 46 | 47 | void timerCallback() override 48 | {} 49 | 50 | protected: 51 | Knob cutoff, q, quality; 52 | }; 53 | } -------------------------------------------------------------------------------- /Source/gui/TextEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Button.h" 3 | #include "GUIParams.h" 4 | 5 | namespace gui 6 | { 7 | struct TextEditor : 8 | public Comp, 9 | public Timer 10 | { 11 | /* tooltip, empty string */ 12 | TextEditor(Utils&, const String&, Notify&&, const String& = "enter value.."); 13 | 14 | /* tooltip, empty string */ 15 | TextEditor(Utils&, const String&, const String&); 16 | 17 | void setVisible(bool) override; 18 | 19 | void addText(const String&); 20 | 21 | void enable(); 22 | 23 | bool isEnabled() const noexcept; 24 | 25 | void disable(); 26 | 27 | const String& getText() const noexcept; 28 | 29 | void setText(const String&); 30 | 31 | bool isEmpty() const noexcept; 32 | 33 | bool isNotEmpty() const noexcept; 34 | 35 | void clear(); 36 | 37 | std::function onEscape, onReturn, onType, onRemove, onClick; 38 | protected: 39 | Label label; 40 | String emptyString, txt; 41 | BlinkyBoy blinkyBoy; 42 | int tickIdx; 43 | bool drawTick; 44 | public: 45 | bool multiLine; 46 | 47 | void mouseUp(const Mouse&) override; 48 | 49 | void paint(Graphics&) override; 50 | 51 | void resized() override; 52 | 53 | void updateLabel(); 54 | 55 | void timerCallback() override; 56 | 57 | bool keyPressed(const KeyPress&) override; 58 | 59 | void removeMultiLine(String& text); 60 | }; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /Source/gui/Shader.cpp: -------------------------------------------------------------------------------- 1 | #include "Shader.h" 2 | 3 | gui::Shader::Shader(Utils& u, Component& _comp) : 4 | Timer(), 5 | comp(_comp), 6 | utils(u), 7 | notify(u.getEventSystem()), 8 | bypassed(false) 9 | { 10 | startTimerHz(12); 11 | } 12 | 13 | void gui::Shader::applyEffect(Image& img, Graphics& g, float, float) 14 | { 15 | g.drawImageAt(img, 0, 0, false); 16 | 17 | if (bypassed) 18 | paintBypassed(img, g); 19 | } 20 | 21 | void gui::Shader::paintBypassed(Image& img, Graphics& g) 22 | { 23 | const auto h = static_cast(img.getHeight()) * .5f; 24 | const auto r = static_cast(img.getWidth()); 25 | 26 | PointF left(0.f, h); 27 | PointF right(r, h); 28 | 29 | juce::ColourGradient grad( 30 | Colour(0x00000000), 31 | left, 32 | Colour(0xff000000), 33 | right, 34 | false 35 | ); 36 | g.setGradientFill(grad); 37 | g.fillAll(); 38 | g.setColour(Colours::c(ColourID::Abort)); 39 | g.drawFittedText("bypassed", img.getBounds(), Just::centredRight, 1); 40 | } 41 | 42 | void gui::Shader::timerCallback() 43 | { 44 | bool shallRepaint = false; 45 | 46 | auto b = utils.getParam(PID::Power)->getValue() < .5f; 47 | if (bypassed != b) 48 | { 49 | bypassed = b; 50 | shallRepaint = true; 51 | } 52 | 53 | if (shallRepaint) 54 | { 55 | repaintWithChildren(&comp); 56 | } 57 | } -------------------------------------------------------------------------------- /Source/audio/CombFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "WHead.h" 4 | #include "AudioUtils.h" 5 | #include "PRM.h" 6 | #include "MIDIManager.h" 7 | #include "XenManager.h" 8 | 9 | namespace audio 10 | { 11 | class CombFilter 12 | { 13 | struct DelayFeedback 14 | { 15 | using Lowpass = smooth::Lowpass; 16 | 17 | DelayFeedback(); 18 | 19 | /* Fs, size */ 20 | void prepare(float, int); 21 | 22 | //samples, numChannels, numSamples, wHead, feedbackBuffer[-1,1], dampBuf, readHead 23 | void operator()(float* const*, int, int, const int*, const float*, const float*, const float* const*) noexcept; 24 | 25 | protected: 26 | AudioBuffer ringBuffer; 27 | std::array lowpass; 28 | int size; 29 | }; 30 | 31 | static constexpr float LowestFrequencyHz = 20.f; 32 | 33 | public: 34 | CombFilter(MIDIVoices&, const XenManager&); 35 | 36 | /* sampleRate, blockSize */ 37 | void prepare(float, int); 38 | 39 | /* samples, numChannels, numSamples, feedback ]-1,1[, damp ]0, 22050[hz, retune [-n,n]semi */ 40 | void operator()(float* const*, int, int, float, float, float) noexcept; 41 | 42 | protected: 43 | MIDIVoices& midiVoices; 44 | const XenManager& xenManager; 45 | 46 | WHead writeHead; 47 | AudioBuffer readHeadBuffer; 48 | DelayFeedback delay; 49 | 50 | PRM feedbackP, dampP, retuneP; 51 | 52 | float Fs, sizeF, curDelay, curNote; 53 | int size; 54 | }; 55 | } -------------------------------------------------------------------------------- /Source/gui/ToastComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Label.h" 3 | 4 | namespace gui 5 | { 6 | class ToastComp : 7 | public Label 8 | { 9 | Notify makeNotify(ToastComp& t) 10 | { 11 | return [&toast = t](EvtType type, const void* stuff) 12 | { 13 | if (type == EvtType::Toast) 14 | { 15 | toast.updateBounds(); 16 | 17 | const auto str = static_cast(stuff); 18 | toast.setText(*str); 19 | toast.resized(); 20 | 21 | toast.setVisible(true); 22 | } 23 | else if (type == EvtType::ClickedEmpty) 24 | { 25 | toast.setVisible(false); 26 | } 27 | }; 28 | } 29 | public: 30 | ToastComp(Utils& u) : 31 | Label(u, "", makeNotify(*this)) 32 | { 33 | font = getFontLobster(); 34 | mode = Mode::TextToLabelBounds; 35 | textCID = ColourID::Txt; 36 | tooltip = "Click somewhere else to close the toast."; 37 | 38 | setInterceptsMouseClicks(true, false); 39 | } 40 | 41 | void paint(Graphics& g) override 42 | { 43 | g.setColour(Colours::c(ColourID::Bg)); 44 | g.fillAll(); 45 | Label::paint(g); 46 | } 47 | 48 | void updateBounds() 49 | { 50 | const auto parent = getParentComponent()->getLocalBounds().toFloat(); 51 | const auto w = parent.getWidth(); 52 | const auto x = 0.f; 53 | const auto h = parent.getHeight() * .5f; 54 | const auto y = (parent.getHeight() - h) * .5f; 55 | const BoundsF nBounds(x, y, w, h); 56 | setBounds(nBounds.toNearestInt()); 57 | } 58 | }; 59 | } -------------------------------------------------------------------------------- /Source/gui/MIDIVoicesComp.cpp: -------------------------------------------------------------------------------- 1 | #include "MIDIVoicesComp.h" 2 | 3 | namespace gui 4 | { 5 | MIDIVoicesComp::MIDIVoicesComp(Utils& u) : 6 | Comp(u, "MIDI Voices", CursorType::Default), 7 | voices(u.getMIDIVoicesArray()) 8 | #if PPD_MIDINumVoices > 0 9 | , voicesActive() 10 | #endif 11 | { 12 | #if PPD_MIDINumVoices > 0 13 | startTimerHz(30); 14 | #endif 15 | } 16 | 17 | void MIDIVoicesComp::timerCallback() 18 | { 19 | #if PPD_MIDINumVoices > 0 20 | bool wannaRepaint = false; 21 | 22 | for (auto v = 0; v < PPD_MIDINumVoices; ++v) 23 | { 24 | auto& voice = voices[v]; 25 | auto active = voice.curNote.noteOn; 26 | if (voicesActive[v] != active) 27 | { 28 | voicesActive[v] = active; 29 | wannaRepaint = true; 30 | } 31 | } 32 | 33 | if (wannaRepaint) 34 | repaint(); 35 | #endif 36 | } 37 | #if PPD_MIDINumVoices > 0 38 | void MIDIVoicesComp::paint(Graphics& g) 39 | { 40 | const auto width = static_cast(getWidth()); 41 | const auto height = static_cast(getHeight()); 42 | 43 | auto x = 0.f; 44 | const auto w = width / static_cast(PPD_MIDINumVoices); 45 | const auto y = 0.f; 46 | const auto h = height; 47 | 48 | for (auto v = 0; v < PPD_MIDINumVoices; ++v, x += w) 49 | { 50 | g.setColour(Colours::c(ColourID::Interact)); 51 | g.drawVerticalLine(static_cast(x), y, h); 52 | 53 | if (voicesActive[v]) 54 | g.fillRect(x, y, w, h); 55 | } 56 | } 57 | #else 58 | void MIDIVoicesComp::paint(Graphics&) {} 59 | #endif 60 | } -------------------------------------------------------------------------------- /Source/gui/Shared.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Using.h" 3 | 4 | namespace gui 5 | { 6 | enum class ColourID 7 | { 8 | Bg, 9 | Txt, 10 | Abort, 11 | Interact, 12 | Inactive, 13 | Darken, 14 | Hover, 15 | Transp, 16 | Mod, 17 | Bias, 18 | NumCols 19 | }; 20 | 21 | static constexpr int NumColours = static_cast(ColourID::NumCols); 22 | 23 | class Colours 24 | { 25 | using Array = std::array(ColourID::NumCols)>; 26 | public: 27 | Colours(); 28 | 29 | Colour defaultColour() noexcept; 30 | 31 | void init(Props*); 32 | 33 | bool set(Colour); 34 | 35 | Colour operator()(ColourID) const noexcept; 36 | 37 | Colour operator()(int) const noexcept; 38 | 39 | Colour get(int) const noexcept; 40 | 41 | static Colours c; 42 | private: 43 | Array cols; 44 | Props* props; 45 | 46 | void setInternal(ColourID, Colour) noexcept; 47 | 48 | String coloursID(); 49 | }; 50 | 51 | // GET FONT 52 | 53 | Font getFontNEL(); 54 | Font getFontLobster(); 55 | Font getFontMsMadi(); 56 | Font getFontDosisSemiBold(); 57 | Font getFontDosisBold(); 58 | Font getFontDosisExtraBold(); 59 | Font getFontDosisLight(); 60 | Font getFontDosisExtraLight(); 61 | Font getFontDosisMedium(); 62 | Font getFontDosisRegular(); 63 | Font getFontDosisVariable(); 64 | } -------------------------------------------------------------------------------- /Source/audio/AutoGain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "../arch/Range.h" 5 | 6 | namespace audio 7 | { 8 | struct PinkNoise 9 | { 10 | static constexpr int Size = 1 << 11; 11 | 12 | /* targetDb */ 13 | PinkNoise(float = -24.f); 14 | 15 | float rms() noexcept; 16 | 17 | float* data() noexcept; 18 | 19 | const float* data() const noexcept; 20 | 21 | protected: 22 | std::array noise; 23 | 24 | void synthesizeWhiteNoise() noexcept; 25 | 26 | void pinkenNoise() noexcept; 27 | }; 28 | 29 | struct AutoGain 30 | { 31 | /* sampleRate, blockSize */ 32 | using OnPrepare = std::function; 33 | /* samples, numChannels, numSamples, valP */ 34 | using OnProcess = std::function; 35 | using OnClear = std::function; 36 | using Range = makeRange::Range; 37 | 38 | /* noise, range, numGainSteps */ 39 | AutoGain(PinkNoise&, const Range & = { 0.f, 1.f }, int = 5); 40 | 41 | void evaluate(const OnPrepare&, 42 | const OnProcess&, const OnClear & = []() {}); 43 | 44 | /* smpl, valPDenorm */ 45 | float fromDenorm(float, float) const noexcept; 46 | 47 | /* smpl, valP */ 48 | float operator()(float, float) const noexcept; 49 | 50 | protected: 51 | PinkNoise& noise; 52 | Range range; 53 | std::vector gain; 54 | float numGainStepsF; 55 | int numGainSteps; 56 | bool evaluating; 57 | 58 | /* smpl, valP */ 59 | float processSample(float, float) const noexcept; 60 | }; 61 | 62 | } -------------------------------------------------------------------------------- /Source/audio/EnvelopeFollower.cpp: -------------------------------------------------------------------------------- 1 | #include "EnvelopeFollower.h" 2 | 3 | namespace audio 4 | { 5 | 6 | EnvFol::EnvFol() : 7 | smoothie(0.f), 8 | rise(1.f), 9 | fall(1.f), 10 | env(0.f), 11 | Fs(1.f), 12 | state(State::Fall) 13 | {} 14 | 15 | void EnvFol::prepare(float _Fs) noexcept 16 | { 17 | Fs = _Fs; 18 | switch (state) 19 | { 20 | case State::Rise: return smoothie.makeFromDecayInMs(rise, Fs); 21 | case State::Fall: return smoothie.makeFromDecayInMs(fall, Fs); 22 | } 23 | } 24 | 25 | void EnvFol::operator()(float* buf, const float* smpls, int numSamples, 26 | float _riseInMs, float _fallInMs) noexcept 27 | { 28 | for (auto s = 0; s < numSamples; ++s) 29 | buf[s] = process(smpls[s], _riseInMs, _fallInMs); 30 | } 31 | 32 | float EnvFol::process(float smpl, 33 | float _riseInMs, float _fallInMs) noexcept 34 | { 35 | rise = _riseInMs; 36 | fall = _fallInMs; 37 | 38 | switch (state) 39 | { 40 | case State::Rise: return processRise(smpl); 41 | case State::Fall: return processFall(smpl); 42 | default: return smpl; 43 | } 44 | } 45 | 46 | float EnvFol::processRise(float smpl) noexcept 47 | { 48 | if (env > smpl) 49 | { 50 | state = State::Fall; 51 | smoothie.makeFromDecayInMs(fall, Fs); 52 | } 53 | env = smoothie(smpl); 54 | return env; 55 | } 56 | 57 | float EnvFol::processFall(float smpl) noexcept 58 | { 59 | if (env < smpl) 60 | { 61 | state = State::Rise; 62 | smoothie.makeFromDecayInMs(rise, Fs); 63 | } 64 | env = smoothie(smpl); 65 | return env; 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /Source/gui/Events.cpp: -------------------------------------------------------------------------------- 1 | #include "Events.h" 2 | 3 | namespace evt 4 | { 5 | //SYSTEM::EVT 6 | 7 | System::Evt::Evt(System& _sys) : 8 | sys(_sys), 9 | notifier(nullptr) 10 | { 11 | } 12 | 13 | System::Evt::Evt(System& _sys, const Notify& _notifier) : 14 | sys(_sys), 15 | notifier(_notifier) 16 | { 17 | sys.add(this); 18 | } 19 | 20 | System::Evt::Evt(System& _sys, Notify&& _notifier) : 21 | sys(_sys), 22 | notifier(_notifier) 23 | { 24 | sys.add(this); 25 | } 26 | 27 | System::Evt::Evt(const Evt& other) : 28 | notifier(other.notifier), 29 | sys(other.sys) 30 | { 31 | sys.add(this); 32 | } 33 | 34 | System::Evt::~Evt() 35 | { 36 | sys.remove(this); 37 | } 38 | 39 | void System::Evt::operator()(const Type type, const void* stuff) const 40 | { 41 | sys.notify(type, stuff); 42 | } 43 | 44 | //SYSTEM 45 | 46 | System::System() : 47 | evts() 48 | {} 49 | 50 | void System::notify(const Type type, const void* stuff) 51 | { 52 | for (const auto e : evts) 53 | e->notifier(type, stuff); 54 | } 55 | 56 | void System::add(Evt* e) 57 | { 58 | evts.push_back(e); 59 | } 60 | 61 | void System::remove(const Evt* e) 62 | { 63 | for (auto i = 0; i < evts.size(); ++i) 64 | if (e == evts[i]) 65 | { 66 | evts.erase(evts.begin() + i); 67 | return; 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Source/audio/XenManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../arch/State.h" 3 | #include 4 | #include 5 | 6 | namespace audio 7 | { 8 | struct XenManager 9 | { 10 | XenManager(); 11 | 12 | /* tmprVal, noteVal */ 13 | void setTemperament(float, int) noexcept; 14 | 15 | /* xen, masterTune, baseNote */ 16 | void operator()(float, float, float) noexcept; 17 | 18 | template 19 | Float noteToFreqHz(Float) const noexcept; 20 | 21 | /* note, lowestFreq, highestFreq */ 22 | template 23 | Float noteToFreqHzWithWrap(Float, Float = static_cast(0), Float = static_cast(22000)) const noexcept; 24 | 25 | template 26 | Float freqHzToNote(Float) noexcept; 27 | 28 | float getXen() const noexcept; 29 | 30 | protected: 31 | float xen, masterTune, baseNote; 32 | std::array, PPD_MaxXen + 1> temperaments; 33 | }; 34 | 35 | } 36 | 37 | #include "Oscillator.h" 38 | 39 | namespace audio 40 | { 41 | struct TuningEditorSynth 42 | { 43 | using SIMD = juce::FloatVectorOperations; 44 | 45 | TuningEditorSynth(const XenManager& _xen); 46 | 47 | void loadPatch(sta::State&); 48 | 49 | void savePatch(sta::State&); 50 | 51 | /* Fs, blockSize */ 52 | void prepare(float, int); 53 | 54 | /* samples, numChannels, numSamples */ 55 | void operator()(float* const*, int, int) noexcept; 56 | 57 | std::atomic pitch, gain; 58 | std::atomic noteOn; 59 | protected: 60 | const XenManager& xen; 61 | OscSine osc; 62 | std::vector buffer; 63 | }; 64 | } -------------------------------------------------------------------------------- /Source/gui/Tooltip.cpp: -------------------------------------------------------------------------------- 1 | #include "Tooltip.h" 2 | 3 | namespace gui 4 | { 5 | Tooltip::Tooltip(Utils& _utils, String&& _tooltip) : 6 | Comp(_utils, _tooltip, makeNotify(this), CursorType::Default), 7 | buildDateLabel(utils, static_cast(JucePlugin_Manufacturer) + " Plugins, v: " + static_cast(__DATE__) + " " + static_cast(__TIME__)), 8 | tooltipLabel(utils, "") 9 | { 10 | layout.init 11 | ( 12 | { 1 }, 13 | { 1, 2 } 14 | ); 15 | 16 | buildDateLabel.textCID = ColourID::Hover; 17 | buildDateLabel.just = Just::centredLeft; 18 | buildDateLabel.mode = Label::Mode::TextToLabelBounds; 19 | buildDateLabel.font = getFontDosisVariable(); 20 | tooltipLabel.textCID = ColourID::Txt; 21 | tooltipLabel.just = Just::centredLeft; 22 | tooltipLabel.mode = buildDateLabel.mode; 23 | tooltipLabel.font = buildDateLabel.font; 24 | 25 | addAndMakeVisible(buildDateLabel); 26 | addAndMakeVisible(tooltipLabel); 27 | } 28 | 29 | void Tooltip::updateTooltip(const String* t) 30 | { 31 | tooltipLabel.setText(t == nullptr ? "" : *t); 32 | tooltipLabel.repaint(); 33 | } 34 | 35 | void Tooltip::paint(Graphics&) {} 36 | 37 | void Tooltip::resized() 38 | { 39 | layout.resized(); 40 | 41 | layout.place(tooltipLabel, 0, 1, 1, 1, false); 42 | layout.place(buildDateLabel, 0, 0, 1, 1, false); 43 | } 44 | 45 | Notify Tooltip::makeNotify(Tooltip* ttc) 46 | { 47 | return [ttc](const EvtType type, const void* stuff) 48 | { 49 | if (type == EvtType::TooltipUpdated) 50 | { 51 | const auto str = static_cast(stuff); 52 | ttc->updateTooltip(str); 53 | } 54 | }; 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /Source/audio/DryWetMix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "LatencyCompensation.h" 3 | #include 4 | 5 | namespace audio 6 | { 7 | class DryWetMix 8 | { 9 | enum 10 | { 11 | #if PPDHasGainIn 12 | GainIn, 13 | #endif 14 | Mix, 15 | #if PPDHasGainOut 16 | GainOut, 17 | #endif 18 | NumBufs 19 | }; 20 | 21 | public: 22 | DryWetMix(); 23 | 24 | /* sampleRate, blockSize, latency */ 25 | void prepare(float, int, int); 26 | 27 | /* samples, numChannels, numSamples, gainInP, unityGainP, mixP, gainOutP, polarityP */ 28 | void saveDry 29 | ( 30 | float* const*, int, int, 31 | #if PPDHasGainIn 32 | float, 33 | #if PPDHasUnityGain 34 | float, 35 | #endif 36 | #endif 37 | float 38 | #if PPDHasGainOut 39 | , float 40 | #if PPDHasPolarity 41 | , float 42 | #endif 43 | #endif 44 | ) noexcept; 45 | 46 | /* samples, numChannels, numSamples */ 47 | void processBypass(float* const*, int, int) noexcept; 48 | 49 | #if PPDHasGainOut 50 | /* samples, numChannels, numSamples */ 51 | void processOutGain(float* const*, int, int) const noexcept; 52 | #endif 53 | 54 | /* samples, numChannels, numSamples, delta */ 55 | void processMix(float* const*, int, int 56 | #if PPDHasDelta 57 | , bool 58 | #endif 59 | ) const noexcept; 60 | 61 | protected: 62 | LatencyCompensation latencyCompensation; 63 | 64 | AudioBuffer buffers; 65 | 66 | #if PPDHasGainIn 67 | Smooth gainInSmooth; 68 | #endif 69 | Smooth mixSmooth; 70 | #if PPDHasGainOut 71 | Smooth gainOutSmooth; 72 | #endif 73 | 74 | AudioBuffer dryBuf; 75 | float mixValue, gainOutValue; 76 | bool mixSmoothing, gainOutSmoothing; 77 | }; 78 | } -------------------------------------------------------------------------------- /Source/gui/GUIParams.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Label.h" 3 | 4 | namespace gui 5 | { 6 | static constexpr float SensitiveDrag = .2f; 7 | static constexpr float WheelDefaultSpeed = .02f; 8 | static constexpr float WheelInertia = .9f; 9 | 10 | static constexpr float LockAlpha = .4f; 11 | 12 | struct Parametr : 13 | public Comp 14 | { 15 | struct ModDial : 16 | public Comp 17 | { 18 | enum class State 19 | { 20 | MaxModDepth, 21 | Bias, 22 | NumStates 23 | }; 24 | 25 | static constexpr int NumStates = static_cast(State::NumStates); 26 | 27 | ModDial(Utils&, Parametr&); 28 | 29 | void paint(Graphics&) override; 30 | 31 | State getState() const noexcept; 32 | 33 | protected: 34 | Parametr& parametr; 35 | Label label; 36 | float dragY; 37 | public: 38 | State state; 39 | 40 | void resized() override; 41 | 42 | void mouseDown(const Mouse&) override; 43 | 44 | void mouseDrag(const Mouse&) override; 45 | 46 | void mouseUp(const Mouse&) override; 47 | 48 | }; 49 | 50 | Parametr(Utils&, PID, bool /*_modulatable*/ = true); 51 | 52 | PID getPID() const noexcept; 53 | 54 | void setLocked(bool); 55 | 56 | protected: 57 | Param& param; 58 | ModDial modDial; 59 | float valNorm, maxModDepth, valMod, modBias; 60 | bool locked; 61 | const bool modulatable; 62 | }; 63 | } -------------------------------------------------------------------------------- /Source/gui/HighLevel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ButtonParameterRandomizer.h" 3 | 4 | #if PPDHasPatchBrowser 5 | #include "PatchBrowser.h" 6 | #endif 7 | 8 | #include "LogoComp.h" 9 | #include "Knob.h" 10 | #include "Menu.h" 11 | #include "MIDICCMonitor.h" 12 | #include "MIDIVoicesComp.h" 13 | #include "LowLevel.h" 14 | 15 | #if PPDHasTuningEditor 16 | #include "TuningEditor.h" 17 | #endif 18 | 19 | namespace gui 20 | { 21 | struct HighLevel : 22 | public Comp 23 | { 24 | /* utils, lowlevel, tuningEditor */ 25 | HighLevel(Utils&, LowLevel* 26 | #if PPDHasTuningEditor 27 | , CompWidgetable* 28 | #endif 29 | ); 30 | 31 | void init(); 32 | 33 | void paint(Graphics& g) override; 34 | 35 | void resized() override; 36 | 37 | protected: 38 | #if PPDHasPatchBrowser 39 | PatchBrowser patchBrowser; 40 | ButtonPatchBrowser patchBrowserButton; 41 | #endif 42 | #if PPDHasTuningEditor 43 | TuningEditorButton tuningEditorButton; 44 | #endif 45 | 46 | Knob macro; 47 | #if PPDHasClipper 48 | Button clipper; 49 | #endif 50 | Button modDepthLocked; 51 | Button swapParamWithModDepth; 52 | Button saveModPatch, loadModPatch, removeCurModPatch; 53 | 54 | ButtonParameterRandomizer parameterRandomizer; 55 | #if PPDHasGainIn 56 | Knob gainIn; 57 | #endif 58 | #if PPDHasGainOut 59 | Knob gainOut; 60 | #endif 61 | Knob mix; 62 | #if PPDHasUnityGain && PPDHasGainIn 63 | Button unityGain; 64 | #endif 65 | std::vector> buttonsBottom; 66 | 67 | MIDICCMonitor ccMonitor; 68 | MIDIVoicesComp midiVoices; 69 | 70 | LowLevel* lowLevel; 71 | std::unique_ptr menu; 72 | Button menuButton; 73 | 74 | std::unique_ptr fileChooser; 75 | 76 | Notify makeNotify(HighLevel&); 77 | }; 78 | } -------------------------------------------------------------------------------- /Source/audio/NullNoiseSynth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include "WHead.h" 8 | 9 | 10 | 11 | namespace audio 12 | { 13 | struct NullSynth 14 | { 15 | using File = juce::File; 16 | using UniqueStream = std::unique_ptr; 17 | using SpecLoc = File::SpecialLocationType; 18 | 19 | NullSynth() : 20 | noise() 21 | { 22 | noise.reserve(1 << 17); 23 | UniqueStream inputStream(File::getSpecialLocation(SpecLoc::currentApplicationFile).createInputStream()); 24 | auto& stream = *inputStream; 25 | 26 | while (!stream.isExhausted()) 27 | { 28 | auto smpl = stream.readFloat(); 29 | if (!std::isnan(smpl) && !std::isinf(smpl) && smpl != 0.f) 30 | { 31 | while (smpl < -1.f || smpl > 1.f) 32 | smpl *= .5f; 33 | noise.push_back(smpl); 34 | } 35 | } 36 | } 37 | 38 | void prepare(int blockSize) 39 | { 40 | wHead.prepare(blockSize, static_cast(noise.size())); 41 | } 42 | 43 | void operator()(float** samples, int numChannels, int numSamples) noexcept 44 | { 45 | wHead(numSamples); 46 | 47 | for (auto ch = 0; ch < numChannels; ++ch) 48 | { 49 | auto smpls = samples[ch]; 50 | 51 | for (auto s = 0; s < numSamples; ++s) 52 | { 53 | const auto w = wHead[s]; 54 | 55 | smpls[s] = noise[w]; 56 | } 57 | } 58 | } 59 | 60 | WHead wHead; 61 | std::vector noise; 62 | }; 63 | } 64 | 65 | /* 66 | 67 | this synth makes crappy noise from data that is used in a wrong way. 68 | it's a fun side project. contributions are welcome 69 | 70 | todo: save and load buffer indexes after first opened plugin 71 | 72 | */ -------------------------------------------------------------------------------- /Source/gui/FilterResponseGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Comp.h" 3 | #include 4 | #include 5 | #include 6 | #include "../audio/XenManager.h" 7 | 8 | namespace gui 9 | { 10 | struct FilterResponseGraph : 11 | public Comp, 12 | public Timer 13 | { 14 | using Complex = std::complex; 15 | 16 | #if JUCE_DEBUG 17 | static constexpr int Size = 1 << 10; 18 | #else 19 | static constexpr int Size = 1 << 12; 20 | #endif 21 | static constexpr float SizeF = static_cast(Size); 22 | static constexpr float SizeInv = 1.f / SizeF; 23 | 24 | using Buffer = std::array; 25 | using DFTBuffer = std::array; 26 | 27 | FilterResponseGraph(Utils&); 28 | 29 | void paint(Graphics&) override; 30 | 31 | void resized() override; 32 | 33 | void timerCallback() override; 34 | 35 | void updateResponseCurve(); 36 | 37 | ColourID responseCurveCID; 38 | /* samples, impulse, numSamples */ 39 | std::function processFilters; 40 | std::function needsUpdate; 41 | protected: 42 | Buffer impulse; 43 | Buffer processBuffer; 44 | DFTBuffer dftBuffer; 45 | 46 | Path responseCurve; 47 | 48 | void resetBuffers(); 49 | 50 | void applyForwardDFT(); 51 | 52 | void generateResponseCurve(); 53 | 54 | }; 55 | 56 | struct FilterResponseGraph2 : 57 | public Comp, 58 | public Timer 59 | { 60 | FilterResponseGraph2(Utils&); 61 | 62 | void paint(Graphics&) override; 63 | 64 | void timerCallback(); 65 | 66 | void resized() override; 67 | 68 | ColourID responseCurveCID; 69 | std::function shallUpdate; 70 | /* responseCurve, width, height */ 71 | std::function update; 72 | protected: 73 | Path responseCurve; 74 | }; 75 | } -------------------------------------------------------------------------------- /Source/Editor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gui/Shader.h" 4 | #include "gui/ContextMenu.h" 5 | #include "gui/HighLevel.h" 6 | #include "gui/Tooltip.h" 7 | #include "gui/ToastComponent.h" 8 | 9 | #if PPDHasTuningEditor 10 | #include "gui/TuningEditor.h" 11 | #endif 12 | 13 | namespace gui 14 | { 15 | struct Editor : 16 | public juce::AudioProcessorEditor 17 | { 18 | static constexpr int MinWidth = 100, MinHeight = 100; 19 | 20 | Editor(audio::Processor&); 21 | 22 | ~Editor(); 23 | 24 | void paint(Graphics& g) override; 25 | 26 | void resized() override; 27 | 28 | void mouseEnter(const Mouse&) override; 29 | void mouseExit(const Mouse&) override; 30 | void mouseDown(const Mouse&) override; 31 | void mouseDrag(const Mouse&) override; 32 | void mouseUp(const Mouse&) override; 33 | void mouseWheelMove(const Mouse&, const MouseWheel&) override; 34 | 35 | audio::Processor& audioProcessor; 36 | 37 | protected: 38 | Layout layout; 39 | Utils utils; 40 | 41 | Image bgImage; 42 | Evt notify; 43 | Button imgRefresh; 44 | 45 | Tooltip tooltip; 46 | 47 | Label pluginTitle; 48 | 49 | LowLevel lowLevel; 50 | #if PPDHasTuningEditor 51 | TuningEditor tuningEditor; 52 | #endif 53 | HighLevel highLevel; 54 | 55 | ContextMenuKnobs contextMenuKnobs; 56 | ContextMenuButtons contextMenuButtons; 57 | 58 | TextEditorKnobs editorKnobs; 59 | 60 | ToastComp toast; 61 | 62 | bool bypassed; 63 | Shader shadr; 64 | 65 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Editor) 66 | //JUCE_LEAK_DETECTOR(Editor) 67 | //JUCE_HEAVYWEIGHT_LEAK_DETECTOR(Editor) 68 | private: 69 | void saveBounds(); 70 | 71 | Notify makeNotify(Editor&); 72 | 73 | /* forced */ 74 | void updateBgImage(bool); 75 | }; 76 | } -------------------------------------------------------------------------------- /Source/audio/WaveTable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "AudioUtils.h" 5 | #include "../arch/State.h" 6 | 7 | namespace audio 8 | { 9 | template 10 | struct WaveTable 11 | { 12 | static constexpr float SizeF = static_cast(Size); 13 | static constexpr float SizeInv = 1.f / SizeF; 14 | static constexpr int NumExtraSamples = 4; 15 | static constexpr int FullSize = Size + NumExtraSamples; 16 | 17 | using Table = std::array; 18 | using Func = std::function; 19 | 20 | WaveTable(); 21 | 22 | void create(const Func&) noexcept; 23 | 24 | /* state, key*/ 25 | void savePatch(sta::State&, const String&); 26 | 27 | /* state, key*/ 28 | void loadPatch(sta::State&, const String&); 29 | 30 | /* idx */ 31 | float operator()(int) const noexcept; 32 | 33 | /* phase */ 34 | float operator()(float) const noexcept; 35 | 36 | float* data() noexcept; 37 | 38 | const float* data() const noexcept; 39 | 40 | protected: 41 | Table table; 42 | }; 43 | 44 | template 45 | inline void createWaveTableSine(WaveTable& table) 46 | { 47 | table.create([](float x) 48 | { 49 | return std::sin(x * Pi); 50 | }); 51 | } 52 | 53 | template 54 | inline void createWaveTableSaw(WaveTable& table) 55 | { 56 | table.create([](float x) 57 | { 58 | return x; 59 | }); 60 | } 61 | 62 | template 63 | inline void createWaveTableTriangle(WaveTable& table) 64 | { 65 | table.create([](float x) 66 | { 67 | return 2.f * std::asin(std::sin(x * Pi)) * PiInv; 68 | }); 69 | } 70 | 71 | template 72 | inline void createWaveTableSquare(WaveTable& table) 73 | { 74 | table.create([](float x) 75 | { 76 | return 1.f - 2.f * std::fmod(std::floor(x), 2.f); 77 | }); 78 | } 79 | 80 | template 81 | inline void createWaveTableNoise(WaveTable& table) 82 | { 83 | juce::Random rand; 84 | table.create([r = rand](float x) 85 | { 86 | return r.nextFloat() * 2.f - 1.f; 87 | }); 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /Source/audio/LatencyCompensation.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | LatencyCompensation.cpp 5 | Created: 14 Sep 2022 6:17:08pm 6 | Author: Hans 7 | 8 | ============================================================================== 9 | */ 10 | 11 | #include "LatencyCompensation.h" 12 | 13 | namespace audio 14 | { 15 | LatencyCompensation::LatencyCompensation() : 16 | ring(), 17 | wHead(), 18 | latency(0) 19 | {} 20 | 21 | void LatencyCompensation::prepare(int blockSize, int _latency) 22 | { 23 | latency = _latency; 24 | if (latency != 0) 25 | { 26 | ring.setSize(2, latency, false, true, false); 27 | wHead.prepare(blockSize, latency); 28 | } 29 | else 30 | { 31 | ring.setSize(0, 0); 32 | wHead.prepare(0, 0); 33 | } 34 | } 35 | 36 | void LatencyCompensation::operator()(float* const* dry, float* const* inputSamples, int numChannels, int numSamples) noexcept 37 | { 38 | if (latency != 0) 39 | { 40 | wHead(numSamples); 41 | 42 | for (auto ch = 0; ch < numChannels; ++ch) 43 | { 44 | const auto smpls = inputSamples[ch]; 45 | 46 | auto rng = ring.getWritePointer(ch); 47 | auto dr = dry[ch]; 48 | 49 | for (auto s = 0; s < numSamples; ++s) 50 | { 51 | const auto w = wHead[s]; 52 | const auto r = (w + 1) % latency; 53 | 54 | rng[w] = smpls[s]; 55 | dr[s] = rng[r]; 56 | } 57 | } 58 | } 59 | else 60 | for (auto ch = 0; ch < numChannels; ++ch) 61 | SIMD::copy(dry[ch], inputSamples[ch], numSamples); 62 | } 63 | 64 | void LatencyCompensation::operator()(float* const* samples, int numChannels, int numSamples) noexcept 65 | { 66 | if (latency != 0) 67 | { 68 | wHead(numSamples); 69 | 70 | for (auto ch = 0; ch < numChannels; ++ch) 71 | { 72 | const auto smpls = samples[ch]; 73 | auto rng = ring.getWritePointer(ch); 74 | 75 | for (auto s = 0; s < numSamples; ++s) 76 | { 77 | const auto w = wHead[s]; 78 | const auto r = (w + 1) % latency; 79 | 80 | rng[w] = smpls[s]; 81 | smpls[s] = rng[r]; 82 | } 83 | } 84 | } 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /Source/audio/MIDIManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "MIDILearn.h" 3 | #include 4 | 5 | namespace audio 6 | { 7 | struct MIDIManager 8 | { 9 | MIDIManager(Params&, State&); 10 | 11 | void savePatch(); 12 | 13 | void loadPatch(); 14 | 15 | /* midiBuffer, numSamples */ 16 | void operator()(MIDIBuffer&, int) noexcept; 17 | 18 | MIDILearn midiLearn; 19 | 20 | /* numSamples */ 21 | std::vector> onInit, onEnd, onSample; 22 | /* midiMessage, sampleIndex */ 23 | std::vector> onCC, onNoteOn, onNoteOff, onPitchbend; 24 | /* sampleIndex */ 25 | std::vector> onNoEvt; 26 | protected: 27 | 28 | /* numSamples */ 29 | void processEmpty(int) noexcept; 30 | 31 | /* midiBuffer, numSamples */ 32 | void processBlock(MIDIBuffer&, int) noexcept; 33 | }; 34 | 35 | struct MIDINote 36 | { 37 | float velocity; 38 | int noteNumber; 39 | bool noteOn; 40 | }; 41 | 42 | struct MIDINoteBuffer 43 | { 44 | MIDINoteBuffer(); 45 | 46 | /* blockSize */ 47 | void prepare(int); 48 | 49 | /* newNote, timestamp */ 50 | void processNoteOn(const MIDINote&, int) noexcept; 51 | 52 | /* timestamp */ 53 | void processNoteOff(int) noexcept; 54 | 55 | /* numSamples */ 56 | void process(int) noexcept; 57 | 58 | std::vector buffer; 59 | MIDINote curNote; 60 | int sampleIdx; 61 | }; 62 | 63 | using MIDIVoicesArray = std::array; 64 | 65 | struct MIDIPitchbendBuffer 66 | { 67 | MIDIPitchbendBuffer(); 68 | 69 | /* blockSize */ 70 | void prepare(int); 71 | 72 | void processInit() noexcept; 73 | 74 | /* pitchbend, timestamp */ 75 | void processPitchbend(float, int) noexcept; 76 | 77 | /* numSamples */ 78 | void process(int) noexcept; 79 | 80 | std::vector buffer; 81 | float curPitchbend; 82 | int sampleIdx; 83 | }; 84 | 85 | struct MIDIVoices 86 | { 87 | MIDIVoices(MIDIManager&); 88 | 89 | /* blockSize */ 90 | void prepare(int); 91 | 92 | MIDIVoicesArray voices; 93 | MIDIPitchbendBuffer pitchbendBuffer; 94 | float pitchbendRange; 95 | int voiceIndex; 96 | }; 97 | } -------------------------------------------------------------------------------- /Source/audio/OverdriveReNEO.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "PRM.h" 4 | #include "AutoGain.h" 5 | 6 | namespace audio 7 | { 8 | struct OverdriveReNeo 9 | { 10 | enum PanVec 11 | { 12 | Gain, 13 | DriveL, 14 | DriveR, 15 | DriveC, 16 | NumPanVecs 17 | }; 18 | 19 | using Range = makeRange::Range; 20 | 21 | struct Filter 22 | { 23 | Filter(); 24 | 25 | void reset() noexcept; 26 | 27 | /* hz, Fs */ 28 | void setFreq(float, float) noexcept; 29 | 30 | float operator()(float) noexcept; 31 | 32 | protected: 33 | std::array, 2> filtr; 34 | }; 35 | 36 | static constexpr float DriveBoost = 420.f; 37 | 38 | static constexpr float GlueBoost = 8.f; 39 | static constexpr float GlueBoostInv = 1.f / GlueBoost; 40 | 41 | /* pinkNoise, muffleRange */ 42 | OverdriveReNeo(PinkNoise&, const Range&); 43 | 44 | /* sampleRate, blockSize */ 45 | void prepare(float, int); 46 | 47 | /* samples, numChannels, numSamples, drive[0,1], muffle[20,20k], pan[-1,1], scrap[0,1] */ 48 | void operator()(float* const*, int, int, float, float, float, float) noexcept; 49 | 50 | private: 51 | std::array filtr; 52 | PRM muffled, pan, drive, scrap; 53 | AutoGain muffleGain, driveGain, scrapGain; 54 | 55 | std::array, NumPanVecs> panVecs; 56 | 57 | float Fs; 58 | 59 | void resetFilter() noexcept; 60 | 61 | void initAutoGain(); 62 | 63 | /* smpls, numSamples, muffleBuf, driveBuf, fltr, scrapBuf */ 64 | void processBlockMono(float*, int, const float*, const float*, Filter&, const float*) noexcept; 65 | 66 | /* samples, numChannels, numSamples, muffleBuf, driveBuf, pan, scrapBuf */ 67 | void processBlockStereo(float* const*, int, int, const float*, const float*, float, const float*) noexcept; 68 | 69 | /* x, p, xy */ 70 | float waveshape(float, float, float) const noexcept; 71 | 72 | /* x, ms, ds, scp */ 73 | float applyAutoGain(float, float, float, float) noexcept; 74 | 75 | /* pan, numSamples */ 76 | void updatePanVecs(float, int) noexcept; 77 | }; 78 | } 79 | 80 | /* 81 | 82 | todo: 83 | 84 | autogain not perfect 85 | 86 | add pitch correction? 87 | 88 | steep highpass for remove dc offset? 89 | 90 | pan knob greyed out on mono tracks 91 | 92 | */ -------------------------------------------------------------------------------- /Source/gui/SpectroBeamComp.cpp: -------------------------------------------------------------------------------- 1 | #include "SpectroBeamComp.h" 2 | 3 | namespace gui 4 | { 5 | template 6 | SpectroBeamComp::SpectroBeamComp(Utils& u, SpecBeam& _beam) : 7 | Comp(u, "Spectro Beam", CursorType::Default), 8 | mainColCID(ColourID::Hover), 9 | xen(u.audioProcessor.xenManager), 10 | beam(_beam), 11 | img(Image::RGB, Size, 1, true) 12 | { 13 | setInterceptsMouseClicks(false, false); 14 | startTimerHz(60); 15 | setOpaque(true); 16 | } 17 | 18 | template 19 | void SpectroBeamComp::paint(Graphics& g) 20 | { 21 | g.setImageResamplingQuality(Graphics::lowResamplingQuality); 22 | g.drawImage(img, getLocalBounds().toFloat()); 23 | } 24 | 25 | template 26 | void SpectroBeamComp::timerCallback() 27 | { 28 | auto ready = beam.ready.load(); 29 | if (!ready) 30 | return; 31 | 32 | const auto Fs = static_cast(utils.audioProcessor.getSampleRate()); 33 | const auto fsInv = 1.f / Fs; 34 | const auto colBase = Colours::c(ColourID::Bg); 35 | const auto col = Colours::c(mainColCID); 36 | const auto buf = beam.buffer.data(); 37 | 38 | const auto lowestDb = -12.f; 39 | const auto highestDb = 6.f; 40 | const auto rangeDb = highestDb - lowestDb; 41 | const auto rangeDbInv = 1.f / rangeDb; 42 | 43 | for (auto x = 0; x < Size; ++x) 44 | { 45 | const auto norm = static_cast(x) * SizeInv; 46 | const auto pitch = norm * 128.f; 47 | const auto freqHz = xen.noteToFreqHzWithWrap(pitch + xen.getXen()); 48 | const auto binIdx = freqHz * fsInv * SizeF; 49 | 50 | const auto bin = interpolate::lerp(buf, binIdx); 51 | const auto magDb = audio::gainToDecibel(bin); 52 | const auto magMapped = juce::jlimit(0.f, 1.f, (magDb - lowestDb) * rangeDbInv); 53 | const auto nCol = colBase.interpolatedWith(col, magMapped); 54 | img.setPixelAt(x, 0, nCol); 55 | } 56 | 57 | beam.ready.store(false); 58 | repaint(); 59 | } 60 | 61 | template struct SpectroBeamComp<8>; 62 | template struct SpectroBeamComp<9>; 63 | template struct SpectroBeamComp<10>; 64 | template struct SpectroBeamComp<11>; 65 | template struct SpectroBeamComp<12>; 66 | template struct SpectroBeamComp<13>; 67 | template struct SpectroBeamComp<14>; 68 | template struct SpectroBeamComp<15>; 69 | } -------------------------------------------------------------------------------- /Source/audio/WaveTable.cpp: -------------------------------------------------------------------------------- 1 | #include "WaveTable.h" 2 | #include "../arch/Interpolation.h" 3 | 4 | namespace audio 5 | { 6 | template 7 | WaveTable::WaveTable() : 8 | table() 9 | { 10 | create([](float x) { return std::cos(x * Pi); }); 11 | } 12 | 13 | template 14 | void WaveTable::create(const Func& func) noexcept 15 | { 16 | auto x = -1.f + SizeInv * .5f; 17 | const auto inc = 2.f * SizeInv; 18 | for (auto s = 0; s < Size; ++s, x += inc) 19 | table[s] = func(x); 20 | for (auto i = 0; i < NumExtraSamples; ++i) 21 | table[Size + i] = table[i]; 22 | } 23 | 24 | template 25 | void WaveTable::savePatch(sta::State& state, const String& key) 26 | { 27 | juce::MemoryBlock mb; 28 | const auto dataSize = FullSize * sizeof(float); 29 | mb.append(table.data(), dataSize); 30 | const auto base64 = mb.toBase64Encoding(); 31 | state.set(key, "wt", base64, false); 32 | } 33 | 34 | template 35 | void WaveTable::loadPatch(sta::State& state, const String& key) 36 | { 37 | auto var = state.get(key, "wt"); 38 | if (var != nullptr) 39 | { 40 | const auto base64 = var->toString(); 41 | juce::MemoryBlock mb; 42 | mb.fromBase64Encoding(base64); 43 | #if JUCE_DEBUG 44 | const auto mbSize = mb.getSize(); 45 | #endif 46 | const auto dataSize = FullSize * sizeof(float); 47 | jassert(mbSize == dataSize); 48 | mb.copyTo(table.data(), 0, dataSize); 49 | } 50 | } 51 | 52 | template 53 | float WaveTable::operator()(int idx) const noexcept 54 | { 55 | return table[idx]; 56 | } 57 | 58 | template 59 | float WaveTable::operator()(float phase) const noexcept 60 | { 61 | const auto idx = phase * SizeF; 62 | return interpolate::lerp(table.data(), idx); 63 | } 64 | 65 | template 66 | float* WaveTable::data() noexcept 67 | { 68 | return table.data(); 69 | } 70 | 71 | template 72 | const float* WaveTable::data() const noexcept 73 | { 74 | return table.data(); 75 | } 76 | 77 | template struct WaveTable<1 << 8>; 78 | template struct WaveTable<1 << 9>; 79 | template struct WaveTable<1 << 10>; 80 | template struct WaveTable<1 << 11>; 81 | template struct WaveTable<1 << 12>; 82 | template struct WaveTable<1 << 13>; 83 | } -------------------------------------------------------------------------------- /Source/audio/SpectroBeam.cpp: -------------------------------------------------------------------------------- 1 | #include "SpectroBeam.h" 2 | 3 | namespace audio 4 | { 5 | template 6 | SpectroBeam::SpectroBeam() : 7 | smpls(), 8 | fft(Order), 9 | fifo(), 10 | window(), 11 | buffer(), 12 | ready(false), 13 | idx(0) 14 | { 15 | SIMD::clear(buffer.data(), Size2); 16 | SIMD::clear(fifo.data(), Size2); 17 | 18 | // gaussian window 19 | for (auto i = 0; i < Size; ++i) 20 | { 21 | const auto norm = static_cast(i) * SizeInv; 22 | const auto x = norm * 2.f - 1.f; 23 | const auto w = std::exp(-x * x * 16.f); 24 | window[i] = w; 25 | } 26 | } 27 | 28 | template 29 | void SpectroBeam::prepare(int blockSize) 30 | { 31 | smpls.resize(blockSize); 32 | } 33 | 34 | template 35 | void SpectroBeam::operator()(float** samples, int numChannels, int numSamples) noexcept 36 | { 37 | const auto chInv = 1.f / numChannels; 38 | 39 | for (auto s = 0; s < numSamples; ++s) 40 | { 41 | auto mid = samples[0][s]; 42 | for (auto ch = 1; ch < numChannels; ++ch) 43 | mid += samples[ch][s]; 44 | mid *= chInv; 45 | smpls[s] = mid; 46 | } 47 | 48 | process(numSamples); 49 | } 50 | 51 | template 52 | void SpectroBeam::process(int numSamples) noexcept 53 | { 54 | auto fif = fifo.data(); 55 | for (auto s = 0; s < numSamples; ++s) 56 | { 57 | fif[idx] = smpls[s]; 58 | ++idx; 59 | if (idx == Size) 60 | { 61 | const auto wndw = window.data(); 62 | auto buf = buffer.data(); 63 | 64 | SIMD::multiply(fif, wndw, Size); 65 | fft.performRealOnlyForwardTransform(fif, true); 66 | SIMD::copy(buf, fif, Size); 67 | ready.store(true); 68 | idx = 0; 69 | } 70 | } 71 | } 72 | 73 | template struct SpectroBeam<1>; 74 | template struct SpectroBeam<2>; 75 | template struct SpectroBeam<3>; 76 | template struct SpectroBeam<4>; 77 | template struct SpectroBeam<5>; 78 | template struct SpectroBeam<6>; 79 | template struct SpectroBeam<7>; 80 | template struct SpectroBeam<8>; 81 | template struct SpectroBeam<9>; 82 | template struct SpectroBeam<10>; 83 | template struct SpectroBeam<11>; 84 | template struct SpectroBeam<12>; 85 | template struct SpectroBeam<13>; 86 | template struct SpectroBeam<14>; 87 | template struct SpectroBeam<15>; 88 | } 89 | -------------------------------------------------------------------------------- /Source/arch/State.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace sta 6 | { 7 | class State 8 | { 9 | using String = juce::String; 10 | using ValueTree = juce::ValueTree; 11 | using Var = juce::var; 12 | using Undo = juce::UndoManager; 13 | using XML = std::unique_ptr; 14 | using XMLDoc = juce::XmlDocument; 15 | using Proc = juce::AudioProcessor; 16 | 17 | public: 18 | State(); 19 | 20 | State(const String&); 21 | 22 | void savePatch(const Proc&, juce::MemoryBlock&) const; 23 | 24 | void savePatch(juce::File&) const; 25 | 26 | void loadPatch(const XML&); 27 | 28 | void loadPatch(const Proc&, const void* /*data*/ , int /*sizeInBytes*/); 29 | 30 | void loadPatch(const char* /*data*/, int /*sizeInBytes*/); 31 | 32 | void loadPatch(const juce::File&); 33 | 34 | void loadPatch(const ValueTree&); 35 | 36 | /*key, id, val, undoable*/ 37 | void set(String&& /*key*/, String&& /*id*/, Var&&, bool /*undoable*/ = true); 38 | 39 | /*key, id, val, undoable*/ 40 | void set(String&& /*key*/, const String& /*id*/, Var&&, bool /*undoable*/ = true); 41 | 42 | /*key, id, val, undoable*/ 43 | void set(const String& /*key*/, String&& /*id*/, Var&&, bool /*undoable*/ = true); 44 | 45 | /*key, id, val, undoable*/ 46 | void set(const String& /*key*/, const String& /*id*/, Var&&, bool /*undoable*/ = true); 47 | 48 | /*key, id*/ 49 | const Var* get(String&& /*key*/, String&& /*id*/) const; 50 | 51 | /*key, id*/ 52 | const Var* get(String&& /*key*/, const String& /*id*/) const; 53 | 54 | /*key, id*/ 55 | const Var* get(const String& /*key*/, const String& /*id*/) const; 56 | 57 | void undo(); 58 | 59 | void redo(); 60 | 61 | ValueTree getState() const noexcept; 62 | 63 | // DEBUGGING: 64 | String toString() const; 65 | 66 | void dbg() const; 67 | 68 | String toString(String&& /*key*/, String&& /*id*/) const; 69 | 70 | protected: 71 | ValueTree state; 72 | Undo undoer; 73 | 74 | private: 75 | String toID(const String& /*txt*/) const; 76 | 77 | void setProperty(const String& /*key*/, const String& /*id*/, Var&&, ValueTree /*knot*/, Undo*); 78 | 79 | const Var* getProperty(const String& /*key*/, const String& /*id*/, ValueTree /*knot*/) const; 80 | }; 81 | } -------------------------------------------------------------------------------- /Source/arch/Smooth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace smooth 4 | { 5 | // a block-based parameter smoother. 6 | template 7 | struct Block 8 | { 9 | /* startVal */ 10 | Block(float = 0.f); 11 | 12 | /* bufferOut, bufferIn, numSamples */ 13 | void operator()(Float*, Float*, int) noexcept; 14 | 15 | /* buffer, val, numSamples */ 16 | void operator()(Float*, Float, int) noexcept; 17 | 18 | /* buffer, numSamples */ 19 | void operator()(Float*, int) noexcept; 20 | 21 | Float curVal; 22 | }; 23 | 24 | template 25 | struct Lowpass 26 | { 27 | static constexpr Float Pi = static_cast(3.14159265359); 28 | static constexpr Float Tau = Pi * static_cast(2); 29 | 30 | /* decay */ 31 | static Float getXFromFc(Float) noexcept; 32 | /* decay, Fs */ 33 | static Float getXFromHz(Float, Float) noexcept; 34 | 35 | /* decay */ 36 | void makeFromDecayInSamples(Float) noexcept; 37 | /* decay, Fs */ 38 | void makeFromDecayInSecs(Float, Float) noexcept; 39 | /* decay */ 40 | void makeFromDecayInFc(Float) noexcept; 41 | /* decay, Fs */ 42 | void makeFromDecayInHz(Float, Float) noexcept; 43 | /* decay, Fs */ 44 | void makeFromDecayInMs(Float, Float) noexcept; 45 | 46 | void copyCutoffFrom(const Lowpass&) noexcept; 47 | 48 | /* startVal */ 49 | Lowpass(const Float = static_cast(0)); 50 | 51 | void reset(); 52 | 53 | /* buffer, val, numSamples */ 54 | void operator()(Float*, Float, int) noexcept; 55 | /* buffer, numSamples */ 56 | void operator()(Float*, int/*numSamples*/) noexcept; 57 | /* val */ 58 | Float operator()(Float) noexcept; 59 | 60 | void setX(Float) noexcept; 61 | 62 | Float a0, b1, y1, startVal; 63 | 64 | Float processSample(Float) noexcept; 65 | }; 66 | 67 | template 68 | struct Smooth 69 | { 70 | /* smoothLenMs, Fs, */ 71 | void makeFromDecayInMs(Float, Float) noexcept; 72 | 73 | Smooth(float /*startVal*/ = 0.f); 74 | 75 | /* bufferOut, bufferIn, numSamples */ 76 | void operator()(Float*, Float*, int) noexcept; 77 | 78 | /* buffer, val, numSamples */ 79 | bool operator()(Float*, Float, int) noexcept; 80 | 81 | /* buffer, numSamples */ 82 | bool operator()(Float*, int) noexcept; 83 | 84 | protected: 85 | Block block; 86 | Lowpass lowpass; 87 | Float cur, dest; 88 | bool smoothing; 89 | }; 90 | } -------------------------------------------------------------------------------- /Source/audio/MIDILearn.cpp: -------------------------------------------------------------------------------- 1 | #include "MIDILearn.h" 2 | 3 | namespace audio 4 | { 5 | MIDILearn::CC::CC() : 6 | param(nullptr) 7 | {} 8 | 9 | void MIDILearn::CC::setValue(int _value) 10 | { 11 | auto p = param.load(); 12 | if (p == nullptr) 13 | return; 14 | 15 | const auto value = static_cast(_value) * ValInv; 16 | 17 | p->setValueWithGesture(value); 18 | } 19 | 20 | MIDILearn::MIDILearn(Params& _params, State& _state) : 21 | ccBuf(), 22 | ccIdx(-1), 23 | assignableParam(nullptr), 24 | params(_params), 25 | state(_state), 26 | c(-1) 27 | { 28 | } 29 | 30 | void MIDILearn::savePatch() const 31 | { 32 | for (auto i = 0; i < ccBuf.size(); ++i) 33 | { 34 | const auto& cc = ccBuf[i]; 35 | const auto prm = cc.param.load(); 36 | if (prm != nullptr) 37 | state.set(getIDString(i), "id", param::toID(param::toString(prm->id)), true); 38 | } 39 | } 40 | 41 | void MIDILearn::loadPatch() 42 | { 43 | for (auto i = 0; i < ccBuf.size(); ++i) 44 | { 45 | const auto var = state.get(getIDString(i), "id"); 46 | if (var) 47 | { 48 | const auto idStr = var->toString(); 49 | 50 | auto& cc = ccBuf[i]; 51 | const auto pID = param::toPID(idStr); 52 | cc.param.store(params[pID]); 53 | } 54 | } 55 | } 56 | 57 | void MIDILearn::processBlockInit() noexcept 58 | { 59 | c = -1; 60 | } 61 | 62 | void MIDILearn::processBlockMIDICC(const MIDIMessage& msg) noexcept 63 | { 64 | c = msg.getControllerNumber(); 65 | if (c < ccBuf.size()) 66 | { 67 | auto& cc = ccBuf[c]; 68 | 69 | auto ap = assignableParam.load(); 70 | if (ap != nullptr) 71 | { 72 | cc.param.store(ap); 73 | assignableParam.store(nullptr); 74 | } 75 | 76 | cc.setValue(msg.getControllerValue()); 77 | } 78 | } 79 | 80 | void MIDILearn::processBlockEnd() noexcept 81 | { 82 | if (c != -1) 83 | ccIdx.store(c); 84 | } 85 | 86 | void MIDILearn::assignParam(Param* param) noexcept 87 | { 88 | assignableParam.store(param); 89 | } 90 | 91 | void MIDILearn::removeParam(Param* param) noexcept 92 | { 93 | for (auto& cc : ccBuf) 94 | if (param == cc.param) 95 | cc.param.store(nullptr); 96 | } 97 | 98 | String MIDILearn::getIDString(int idx) const 99 | { 100 | return "midilearn/cc" + String(idx); 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /Source/gui/Oscilloscope.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../audio/Oscilloscope.h" 3 | #include "Button.h" 4 | 5 | namespace gui 6 | { 7 | struct Oscilloscope : 8 | public Comp, 9 | public Timer 10 | { 11 | using Oscope = audio::Oscilloscope; 12 | static constexpr int FPS = 24; 13 | 14 | Oscilloscope(Utils& u, String&& _tooltip, const Oscope& _oscope) : 15 | Comp(u, _tooltip, CursorType::Default), 16 | Timer(), 17 | oscope(_oscope), 18 | curve(), 19 | bipolar(true) 20 | { 21 | startTimerHz(FPS); 22 | } 23 | 24 | void resized() override 25 | { 26 | const auto thicc = utils.thicc; 27 | bounds = getLocalBounds().toFloat().reduced(thicc); 28 | 29 | curve = Path(); 30 | curve.preallocateSpace(static_cast(bounds.getWidth()) + 1); 31 | } 32 | 33 | void paint(Graphics& g) override 34 | { 35 | const auto thicc = utils.thicc; 36 | Stroke stroke(thicc, Stroke::JointStyle::beveled, Stroke::EndCapStyle::rounded); 37 | 38 | g.setColour(Colours::c(ColourID::Darken)); 39 | g.fillRoundedRectangle(bounds, thicc); 40 | 41 | const auto data = oscope.data(); 42 | const auto size = oscope.windowLength(); 43 | const auto sizeF = static_cast(size); 44 | const auto beatLength = oscope.getBeatLength(); 45 | const auto w = bounds.getWidth(); 46 | const auto h = bounds.getHeight(); 47 | const auto xScale = w / std::min(beatLength, sizeF); 48 | const auto bipolarVal = bipolar ? 1.f : 0.f; 49 | const auto yScale = h - bipolarVal * (h * .5f - h); 50 | const auto xScaleInv = 1.f / xScale; 51 | const auto xOff = bounds.getX(); 52 | const auto yOff = bounds.getY() + yScale; 53 | 54 | curve.clear(); 55 | auto y = yOff - data[0] * yScale; 56 | curve.startNewSubPath(xOff, y); 57 | for (auto i = 1.f; i <= w; ++i) 58 | { 59 | const auto x = xOff + i; 60 | const auto idx = static_cast(i * xScaleInv); 61 | y = yOff - data[idx] * yScale; 62 | curve.lineTo(x, y); 63 | } 64 | 65 | g.setColour(Colours::c(ColourID::Txt)); 66 | g.strokePath(curve, stroke); 67 | } 68 | 69 | void timerCallback() override 70 | { 71 | repaint(); 72 | } 73 | 74 | protected: 75 | const Oscope& oscope; 76 | BoundsF bounds; 77 | Path curve; 78 | public: 79 | bool bipolar; 80 | }; 81 | } 82 | 83 | /* 84 | 85 | todo: 86 | 87 | performance: only repaint part if the interface that was changed 88 | 89 | */ -------------------------------------------------------------------------------- /Source/gui/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Using.h" 3 | #include "Shared.h" 4 | #include "Events.h" 5 | #include "../audio/MIDILearn.h" 6 | 7 | namespace gui 8 | { 9 | using Notify = evt::Notify; 10 | using Evt = evt::System::Evt; 11 | using EvtType = evt::Type; 12 | using EventSystem = evt::System; 13 | 14 | enum class CursorType 15 | { 16 | Default, 17 | Interact, 18 | Inactive, 19 | Mod, 20 | Bias, 21 | NumTypes 22 | }; 23 | 24 | juce::MouseCursor makeCursor(CursorType); 25 | 26 | void hideCursor(); 27 | void showCursor(const Component&); 28 | void centreCursor(const Component&, juce::MouseInputSource&); 29 | 30 | class Utils 31 | { 32 | static constexpr float DragSpeed = .5f; 33 | public: 34 | Utils(Component& /*pluginTop*/, Processor&); 35 | 36 | Param* getParam(PID pID) noexcept; 37 | const Param* getParam(PID pID) const noexcept; 38 | 39 | /* pID, offset */ 40 | Param* getParam(PID pID, int) noexcept; 41 | /* pID, offset */ 42 | const Param* getParam(PID pID, int) const noexcept; 43 | 44 | std::vector& getAllParams() noexcept; 45 | const std::vector& getAllParams() const noexcept; 46 | 47 | Params& getParams() noexcept; 48 | const Params& getParams() const noexcept; 49 | 50 | juce::ValueTree getState() const noexcept; 51 | 52 | void assignMIDILearn(PID pID) noexcept; 53 | void removeMIDILearn(PID pID) noexcept; 54 | const audio::MIDILearn& getMIDILearn() const noexcept; 55 | 56 | float getDragSpeed() const noexcept; 57 | 58 | float fontHeight() const noexcept; 59 | 60 | EventSystem& getEventSystem(); 61 | 62 | const std::atomic& getMeter(int i) const noexcept; 63 | 64 | Point getScreenPosition() const noexcept; 65 | 66 | void resized(); 67 | 68 | ValueTree savePatch(); 69 | 70 | void loadPatch(const ValueTree&); 71 | 72 | AppProps& getProps() noexcept; 73 | 74 | const MIDIVoicesArray& getMIDIVoicesArray() const noexcept 75 | { 76 | return audioProcessor.midiVoices.voices; 77 | } 78 | 79 | void giveDAWKeyboardFocus(); 80 | 81 | Component& pluginTop; 82 | float thicc; 83 | Processor& audioProcessor; 84 | protected: 85 | Params& params; 86 | EventSystem eventSystem; 87 | Evt evt; 88 | }; 89 | 90 | void appendRandomString(String&, Random&, int/*length*/, 91 | const String& /*legalChars*/ = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); 92 | 93 | /*number to snap, max order of sequence 1 << x*/ 94 | int snapToJordanPolyaSequence(int, int) noexcept; 95 | } -------------------------------------------------------------------------------- /Source/fonts/Dosis/README.txt: -------------------------------------------------------------------------------- 1 | Dosis Variable Font 2 | =================== 3 | 4 | This download contains Dosis as both a variable font and static fonts. 5 | 6 | Dosis is a variable font with this axis: 7 | wght 8 | 9 | This means all the styles are contained in a single file: 10 | Dosis/Dosis-VariableFont_wght.ttf 11 | 12 | If your app fully supports variable fonts, you can now pick intermediate styles 13 | that aren’t available as static fonts. Not all apps support variable fonts, and 14 | in those cases you can use the static font files for Dosis: 15 | Dosis/static/Dosis-ExtraLight.ttf 16 | Dosis/static/Dosis-Light.ttf 17 | Dosis/static/Dosis-Regular.ttf 18 | Dosis/static/Dosis-Medium.ttf 19 | Dosis/static/Dosis-SemiBold.ttf 20 | Dosis/static/Dosis-Bold.ttf 21 | Dosis/static/Dosis-ExtraBold.ttf 22 | 23 | Get started 24 | ----------- 25 | 26 | 1. Install the font files you want to use 27 | 28 | 2. Use your app's font picker to view the font family and all the 29 | available styles 30 | 31 | Learn more about variable fonts 32 | ------------------------------- 33 | 34 | https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts 35 | https://variablefonts.typenetwork.com 36 | https://medium.com/variable-fonts 37 | 38 | In desktop apps 39 | 40 | https://theblog.adobe.com/can-variable-fonts-illustrator-cc 41 | https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts 42 | 43 | Online 44 | 45 | https://developers.google.com/fonts/docs/getting_started 46 | https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide 47 | https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts 48 | 49 | Installing fonts 50 | 51 | MacOS: https://support.apple.com/en-us/HT201749 52 | Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux 53 | Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows 54 | 55 | Android Apps 56 | 57 | https://developers.google.com/fonts/docs/android 58 | https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts 59 | 60 | License 61 | ------- 62 | Please read the full license text (OFL.txt) to understand the permissions, 63 | restrictions and requirements for usage, redistribution, and modification. 64 | 65 | You can use them freely in your products & projects - print or digital, 66 | commercial or otherwise. 67 | 68 | This isn't legal advice, please consider consulting a lawyer and see the full 69 | license for all details. 70 | -------------------------------------------------------------------------------- /Source/gui/Label.cpp: -------------------------------------------------------------------------------- 1 | #include "Label.h" 2 | 3 | namespace gui 4 | { 5 | Label::Label(Utils& u, const String& _text, Notify&& _notify) : 6 | Comp(u, "", std::move(_notify), CursorType::Default), 7 | group(), 8 | textCID(ColourID::Txt), 9 | just(Just::centred), 10 | font(getFontDosisExtraBold()), 11 | minFontHeight(12.f), 12 | mode(Mode::WindowToTextBounds), 13 | text(_text) 14 | { 15 | font.setHeight(minFontHeight); 16 | setInterceptsMouseClicks(false, false); 17 | } 18 | 19 | void Label::setText(const String& txt) 20 | { 21 | if (txt == text) 22 | return; 23 | 24 | text = txt; 25 | 26 | if (empty() || getWidth() == 0 || getHeight() == 0) 27 | return; 28 | 29 | updateTextBounds(); 30 | } 31 | 32 | const String& Label::getText() const 33 | { 34 | return text; 35 | } 36 | 37 | void Label::setMinFontHeight(float h) 38 | { 39 | minFontHeight = h; 40 | updateTextBounds(); 41 | } 42 | 43 | bool Label::empty() const noexcept 44 | { 45 | return text.isEmpty(); 46 | } 47 | 48 | void Label::paint(Graphics& g) 49 | { 50 | const auto bounds = getLocalBounds().toFloat(); 51 | g.setColour(Colours::c(textCID)); 52 | g.setFont(font); 53 | g.drawFittedText(text, bounds.toNearestInt(), just, 1); 54 | } 55 | 56 | void Label::resized() 57 | { 58 | updateTextBounds(); 59 | } 60 | 61 | void Label::updateTextBounds() 62 | { 63 | float nHeight = minFontHeight; 64 | 65 | if (mode == Mode::WindowToTextBounds) 66 | { 67 | const auto val = utils.fontHeight(); 68 | nHeight = std::max(nHeight, val); 69 | } 70 | 71 | else if (mode == Mode::TextToLabelBounds) 72 | { 73 | const auto thicc = utils.thicc; 74 | const auto width = static_cast(getWidth()); 75 | const auto height = static_cast(getHeight()); 76 | 77 | const auto fontBounds = boundsOf(font, text); 78 | 79 | if (fontBounds.getWidth() != 0.f) 80 | { 81 | const PointF dif 82 | ( 83 | fontBounds.getWidth() - width, 84 | fontBounds.getHeight() - height 85 | ); 86 | 87 | float ratio; 88 | if (dif.x > dif.y) 89 | ratio = width / fontBounds.getWidth(); 90 | else 91 | ratio = height / fontBounds.getHeight(); 92 | 93 | nHeight = std::max(minFontHeight, font.getHeight() * ratio - thicc); 94 | } 95 | } 96 | 97 | else if (mode == Mode::None) 98 | { 99 | font.setHeight(nHeight); 100 | } 101 | 102 | font.setHeight(nHeight); 103 | } 104 | 105 | } 106 | 107 | -------------------------------------------------------------------------------- /Source/audio/MIDIDelay.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AudioUtils.h" 3 | 4 | namespace audio 5 | { 6 | class MIDIDelay 7 | { 8 | static constexpr int NumEvents = 64; 9 | 10 | struct Evt 11 | { 12 | Evt() : 13 | msg(MIDIMessage::noteOn(1, 0, 0.f)), 14 | time(0), 15 | used(false) 16 | {} 17 | 18 | MIDIMessage msg; 19 | int time; 20 | bool used; 21 | }; 22 | 23 | public: 24 | MIDIDelay() : 25 | evts(), 26 | outputBuffer() 27 | { 28 | outputBuffer.ensureSize(NumEvents); 29 | } 30 | 31 | void operator()(MIDIBuffer& midi, int numSamples, int delayTimeSamples) noexcept 32 | { 33 | if (delayTimeSamples < 1) 34 | return; 35 | 36 | for (auto itRef : midi) 37 | { 38 | auto msg = itRef.getMessage(); 39 | midiToEvts(msg, itRef.samplePosition + delayTimeSamples); 40 | } 41 | 42 | outputBuffer.clear(); 43 | 44 | for (auto s = 0; s < numSamples; ++s) 45 | evtsToOutput(s); 46 | 47 | midi.swapWith(outputBuffer); 48 | } 49 | 50 | protected: 51 | std::array evts; 52 | MIDIBuffer outputBuffer; 53 | 54 | void midiToEvts(MIDIMessage& msg, int time) noexcept 55 | { 56 | for (auto i = 0; i < NumEvents; ++i) 57 | { 58 | auto& evt = evts[i]; 59 | 60 | if (!evt.used) 61 | { 62 | evt.time = time; 63 | evt.msg = msg; 64 | evt.used = true; 65 | return; 66 | } 67 | } 68 | } 69 | 70 | void evtsToOutput(int s) noexcept 71 | { 72 | for (auto i = 0; i < NumEvents; ++i) 73 | { 74 | auto& evt = evts[i]; 75 | 76 | if (evt.used) 77 | { 78 | --evt.time; 79 | if (evt.time < 1) 80 | { 81 | if (outputBuffer.addEvent(evt.msg, s)) 82 | { 83 | evt.used = false; 84 | } 85 | } 86 | } 87 | } 88 | 89 | } 90 | }; 91 | } 92 | 93 | /* 94 | 95 | to do: 96 | 97 | if lookahead enabled, gui needs to show if attack parameter longer than possible latency 98 | 99 | */ -------------------------------------------------------------------------------- /Source/gui/Using.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BinaryData.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../Processor.h" 12 | #include "../param/Param.h" 13 | #include "../arch/State.h" 14 | #include "../audio/MIDIManager.h" 15 | 16 | #include 17 | #include 18 | 19 | namespace gui 20 | { 21 | using Colour = juce::Colour; 22 | using Gradient = juce::ColourGradient; 23 | using String = juce::String; 24 | using Font = juce::Font; 25 | using Props = juce::PropertiesFile; 26 | using AppProps = juce::ApplicationProperties; 27 | using Cursor = juce::MouseCursor; 28 | using Image = juce::Image; 29 | using Graphics = juce::Graphics; 30 | using Mouse = juce::MouseEvent; 31 | using MouseWheel = juce::MouseWheelDetails; 32 | using Graphics = juce::Graphics; 33 | using Just = juce::Justification; 34 | using Timer = juce::Timer; 35 | using Path = juce::Path; 36 | using Point = juce::Point; 37 | using PointF = juce::Point; 38 | using Bounds = juce::Rectangle; 39 | using BoundsF = juce::Rectangle; 40 | using Line = juce::Line; 41 | using LineF = juce::Line; 42 | using Image = juce::Image; 43 | using Stroke = juce::PathStrokeType; 44 | using Affine = juce::AffineTransform; 45 | using Random = juce::Random; 46 | using KeyPress = juce::KeyPress; 47 | using ValueTree = juce::ValueTree; 48 | using File = juce::File; 49 | using PropertiesFile = juce::PropertiesFile; 50 | using RangedDirectoryIterator = juce::RangedDirectoryIterator; 51 | using Identifier = juce::Identifier; 52 | using Drawable = juce::Drawable; 53 | using UniqueDrawable = std::unique_ptr; 54 | using RangeF = juce::NormalisableRange; 55 | using Time = juce::Time; 56 | using FileChooser = juce::FileChooser; 57 | using Component = juce::Component; 58 | using SystemClipboard = juce::SystemClipboard; 59 | using SIMD = juce::FloatVectorOperations; 60 | 61 | using Processor = audio::Processor; 62 | 63 | using PID = param::PID; 64 | using Param = param::Param; 65 | using Params = param::Params; 66 | using MIDIVoicesArray = audio::MIDIVoicesArray; 67 | 68 | static constexpr float Tau = 6.28318530718f; 69 | static constexpr float Pi = Tau * .5f;; 70 | static constexpr float PiHalf = Tau * .25f; 71 | static constexpr float PiQuart = Tau * .125f; 72 | } -------------------------------------------------------------------------------- /Source/audio/Oscillator.cpp: -------------------------------------------------------------------------------- 1 | #include "Oscillator.h" 2 | #include "../arch/Conversion.h" 3 | 4 | namespace audio 5 | { 6 | // OscSine 7 | 8 | template 9 | OscSine::OscSine() : 10 | phasor() 11 | {} 12 | 13 | template 14 | void OscSine::prepare(Float fsInv) 15 | { 16 | phasor.prepare(fsInv); 17 | } 18 | 19 | template 20 | void OscSine::setFreqHz(Float hz) 21 | { 22 | phasor.setFrequencyHz(hz); 23 | } 24 | 25 | template 26 | void OscSine::reset(Float phase) 27 | { 28 | phasor.reset(phase); 29 | } 30 | 31 | template 32 | Float* OscSine::operator()(Float* buffer, int numSamples) noexcept 33 | { 34 | for (auto s = 0; s < numSamples; ++s) 35 | buffer[s] = synthesizeSample(); 36 | return buffer; 37 | } 38 | 39 | template 40 | Float OscSine::operator()() noexcept 41 | { 42 | return synthesizeSample(); 43 | } 44 | 45 | template 46 | Float OscSine::synthesizeSample() 47 | { 48 | const auto phase = phasor().phase; 49 | return std::cos(phase * static_cast(Tau)); 50 | } 51 | 52 | template struct OscSine; 53 | template struct OscSine; 54 | 55 | // RingModSimple 56 | 57 | template 58 | RingModSimple::RingModSimple() : 59 | osc(), 60 | freqBuffer(), 61 | freqSmooth{ 20.f, 20.f } 62 | {} 63 | 64 | template 65 | void RingModSimple::prepare(Float Fs, int blockSize) 66 | { 67 | const auto fsInv = static_cast(1) / Fs; 68 | 69 | for (auto& osci : osc) 70 | osci.prepare(fsInv); 71 | freqBuffer.setSize(2, blockSize, false, false, false); 72 | for (auto& f : freqSmooth) 73 | f.makeFromDecayInMs(static_cast(20), Fs); 74 | } 75 | 76 | template 77 | void RingModSimple::operator()(Float** samples, int numChannels, int numSamples, 78 | Float** _freq) noexcept 79 | { 80 | auto freqBufs = freqBuffer.getArrayOfWritePointers(); 81 | 82 | for (auto ch = 0; ch < numChannels; ++ch) 83 | { 84 | auto freq = _freq[ch]; 85 | 86 | auto smpls = samples[ch]; 87 | auto freqBuf = freqBufs[ch]; 88 | auto& osci = osc[ch]; 89 | 90 | freqSmooth[ch](freqBuf, freq, numSamples); 91 | 92 | for (auto s = 0; s < numSamples; ++s) 93 | { 94 | const auto smpl = smpls[s]; 95 | osci.setFreqHz(freqBuf[s]); 96 | const auto mod = osci(); 97 | 98 | smpls[s] = smpl * mod; 99 | } 100 | } 101 | } 102 | 103 | template struct RingModSimple; 104 | template struct RingModSimple; 105 | } -------------------------------------------------------------------------------- /Source/audio/Filter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../arch/Conversion.h" 3 | #include 4 | #include 5 | 6 | namespace audio 7 | { 8 | struct FilterBandpass 9 | { 10 | /* startVal */ 11 | FilterBandpass(float = 0.f); 12 | 13 | void clear() noexcept; 14 | 15 | /* frequency fc [0, .5[, q-factor q [1, 160..] */ 16 | void setFc(float, float) noexcept; 17 | 18 | void copy(const FilterBandpass&) noexcept; 19 | 20 | float operator()(float) noexcept; 21 | 22 | float processSample(float) noexcept; 23 | 24 | /* scaledFreq */ 25 | std::complex response(float) const noexcept; 26 | /* scaledFreq */ 27 | float responseDb(float) const noexcept; 28 | 29 | protected: 30 | float alpha, cosOmega; 31 | float a0, a1, a2, b0, b1, b2; 32 | float x1, x2, y1, y2; 33 | 34 | void updateCoefficients() noexcept; 35 | }; 36 | 37 | template 38 | struct FilterBandpassSlope 39 | { 40 | FilterBandpassSlope(); 41 | 42 | void clear() noexcept; 43 | 44 | void setStage(int) noexcept; 45 | 46 | /* frequency fc [0, .5[, q-factor q [1, 160..] */ 47 | void setFc(float fc, float q) noexcept; 48 | 49 | void copy(FilterBandpassSlope&) noexcept; 50 | 51 | float operator()(float) noexcept; 52 | 53 | /* scaledFreq [0, 22050[ */ 54 | std::complex response(float) const noexcept; 55 | 56 | protected: 57 | std::array filters; 58 | int stage; 59 | }; 60 | 61 | ////////////////////////////////////////////////////////////////// 62 | 63 | struct IIR 64 | { 65 | enum class Type 66 | { 67 | LP, 68 | HP, 69 | BP, 70 | BR, 71 | AP, 72 | LS, 73 | HS, 74 | Notch, 75 | Bell, 76 | NumTypes 77 | }; 78 | 79 | /* startVal */ 80 | IIR(float = 0.f); 81 | 82 | void clear() noexcept; 83 | 84 | /* type, frequency fc [0, .5[, q-factor q [1, 160..] */ 85 | void setFc(Type, float, float) noexcept; 86 | 87 | /* frequency fc [0, .5[, q-factor q [1, 160..] */ 88 | void setFcBP(float, float) noexcept; 89 | 90 | /* frequency fc [0, .5[, q-factor q [1, 160..] */ // doesn't work yet 91 | void setFcLP(float, float) noexcept; 92 | 93 | /* frequency fc [0, .5[, q-factor q [1, 160..] */ // doesn't work yet 94 | void setFcHP(float, float) noexcept; 95 | 96 | void copy(const IIR&) noexcept; 97 | 98 | float operator()(float) noexcept; 99 | 100 | float processSample(float) noexcept; 101 | 102 | /* scaledFreq */ 103 | std::complex response(float) const noexcept; 104 | /* scaledFreq */ 105 | float responseDb(float) const noexcept; 106 | 107 | protected: 108 | float alpha, cosOmega; 109 | float a0, a1, a2, b0, b1, b2; 110 | float x1, x2, y1, y2; 111 | }; 112 | } -------------------------------------------------------------------------------- /Source/audio/Oversampling.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AudioUtils.h" 3 | #include "WHead.h" 4 | #include 5 | #include 6 | 7 | namespace audio 8 | { 9 | struct ImpulseResponse 10 | { 11 | ImpulseResponse(const std::vector & = { 1.f }); 12 | 13 | ImpulseResponse(const ImpulseResponse&); 14 | 15 | void operator=(const std::vector&); 16 | 17 | float operator[](int i) const noexcept; 18 | 19 | const size_t size() const noexcept; 20 | 21 | const int getLatency() const noexcept; 22 | 23 | const float* data() const noexcept; 24 | protected: 25 | std::vector buf; 26 | int latency; 27 | }; 28 | 29 | /* 30 | * Fs,fc,bw,upsampling 31 | Nyquist == Fs / 2 32 | fc < Nyquist 33 | bw < Nyquist 34 | fc + bw < Nyquist 35 | */ 36 | //std::vector makeWindowedSinc(float, float, float, bool); 37 | 38 | /* 39 | * Fs,fc,upsampling 40 | Nyquist == Fs / 2 41 | fc < Nyquist 42 | */ 43 | //std::vector makeWindowedSinc(float, float, bool); 44 | 45 | struct Convolver 46 | { 47 | Convolver(const ImpulseResponse&, const WHead&); 48 | 49 | void prepare(); 50 | 51 | /* samples, numChannels, numSamples */ 52 | void processBlock(float* const*, int, int) noexcept; 53 | 54 | protected: 55 | AudioBuffer ring; 56 | const ImpulseResponse& ir; 57 | const WHead& wHead; 58 | int irSize; 59 | 60 | private: 61 | /*smpls,rng,numSamples*/ 62 | void processBlock(float*, float*, int) noexcept; 63 | /*smpl,rng,w*/ 64 | float processSample(float, float*, int) noexcept; 65 | }; 66 | 67 | /* samplesUp, samplesIn, numChannels, numSamples1x */ 68 | //void zeroStuff(float* const*, const float**, int, int) noexcept; 69 | 70 | /* samplesOut, samplesUp, numChannels, numSamples1x */ 71 | //void decimate(float* const*, const float**, int, int) noexcept; 72 | 73 | class Oversampler 74 | { 75 | static constexpr float CutoffFreq = 18000.f; 76 | public: 77 | Oversampler(); 78 | 79 | Oversampler(Oversampler&); 80 | 81 | /*sampleRate,blockSize*/ 82 | void prepare(const double, const int); 83 | 84 | /*inputBuffer*/ 85 | AudioBuffer& upsample(AudioBuffer&) noexcept; 86 | 87 | /*outputBuffer*/ 88 | void downsample(AudioBuffer&) noexcept; 89 | 90 | const int getLatency() const noexcept; 91 | 92 | double getFsUp() const noexcept; 93 | 94 | int getBlockSizeUp() const noexcept; 95 | 96 | bool isEnabled() const noexcept; 97 | 98 | /* only call this if processor is suspended! */ 99 | void setEnabled(bool) noexcept; 100 | protected: 101 | double Fs; 102 | int blockSize; 103 | 104 | AudioBuffer buffer; 105 | 106 | ImpulseResponse irUp, irDown; 107 | WHead wHead; 108 | Convolver filterUp, filterDown; 109 | 110 | double FsUp; 111 | int blockSizeUp; 112 | 113 | int numSamples1x, numSamples2x; 114 | 115 | std::atomic enabled; 116 | bool enbld; 117 | }; 118 | 119 | } -------------------------------------------------------------------------------- /Source/audio/AbsorbProcessor.cpp: -------------------------------------------------------------------------------- 1 | #include "AbsorbProcessor.h" 2 | 3 | namespace audio 4 | { 5 | AbsorbProcessor::Textures::Textures() : 6 | rm(0.f), 7 | am(0.f), 8 | shapr(0.f), 9 | crushr(0.f), 10 | foldr(0.f) 11 | {} 12 | 13 | void AbsorbProcessor::Textures::prepare(float sampleRate, int blockSize) 14 | { 15 | rm.prepare(sampleRate, blockSize, 10.f); 16 | am.prepare(sampleRate, blockSize, 10.f); 17 | shapr.prepare(sampleRate, blockSize, 10.f); 18 | crushr.prepare(sampleRate, blockSize, 10.f); 19 | foldr.prepare(sampleRate, blockSize, 10.f); 20 | } 21 | 22 | void AbsorbProcessor::Textures::operator()(float** samples, int numChannels, int numSamples, 23 | float** samplesSC, int numChannelsSC, 24 | float _rm, float _am, float _shapr, float _crushr, float _foldr) noexcept 25 | { 26 | auto rmBuf = rm(Decibels::decibelsToGain(_rm, -20.f), numSamples); 27 | auto amBuf = am(Decibels::decibelsToGain(_am, -20.f), numSamples); 28 | auto shaprBuf = shapr(Decibels::decibelsToGain(_shapr, -40.f), numSamples); 29 | auto crushrBuf = crushr(Decibels::decibelsToGain(_crushr, -40.f), numSamples); 30 | auto foldrBuf = foldr(Decibels::decibelsToGain(_foldr, -40.f), numSamples); 31 | 32 | for (auto ch = 0; ch < numChannels; ++ch) 33 | { 34 | const auto chSC = ch % numChannelsSC; 35 | const auto smplsSC = samplesSC[chSC]; 36 | 37 | auto smpls = samples[ch]; 38 | 39 | float A, B, C, D, E; 40 | 41 | for (auto s = 0; s < numSamples; ++s) 42 | { 43 | const auto main = smpls[s]; 44 | const auto sc = smplsSC[s]; 45 | 46 | A = main * sc * rmBuf[s]; 47 | 48 | const auto scsc = sc * sc; 49 | const auto scscSqrt = std::sqrt(scsc); 50 | 51 | B = main * scscSqrt * amBuf[s]; 52 | 53 | if (sc == 0.f) 54 | { 55 | C = D = E = 0.f; 56 | } 57 | else 58 | { 59 | const auto mainInvSc = main / sc; 60 | 61 | C = std::tanh(mainInvSc) * sc * shaprBuf[s]; 62 | D = std::round(mainInvSc) * sc * crushrBuf[s]; 63 | 64 | E = std::fmod(main, sc) * sc * foldrBuf[s]; 65 | } 66 | 67 | smpls[s] = A + B + C + D + E; 68 | } 69 | } 70 | } 71 | 72 | // 73 | 74 | AbsorbProcessor::AbsorbProcessor() : 75 | textures() 76 | { 77 | } 78 | 79 | void AbsorbProcessor::prepare(float sampleRate, int blockSize) 80 | { 81 | textures.prepare(sampleRate, blockSize); 82 | } 83 | 84 | void AbsorbProcessor::operator()(float** samples, int numChannels, int numSamples, 85 | float** samplesSC, int numChannelsSC, 86 | float _rm, float _am, float _shapr, float _crushr, float _foldr) noexcept 87 | { 88 | textures(samples, numChannels, numSamples, samplesSC, numChannelsSC, 89 | _rm, _am, _shapr, _crushr, _foldr); 90 | } 91 | } -------------------------------------------------------------------------------- /Source/gui/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <txt x=".1" y="1.1" w="2.8" h=".8" just="topLeft" text= 5 | "Welcome to the options menu of my plugin :) 6 | 7 | This is where you find all the options 8 | that kinda have to exist, but shouldn't 9 | be in your way on the main interface. 10 | 11 | Click on the nav tabs to navigate 12 | to the different pages!" 13 | /> 14 | </menu> 15 | <menu id="Controls" x="1" y="1;5" tooltip="Get help! Literally."> 16 | <title x="0" y="0" w="1" h="1" /> 17 | <juxtaposition x="0" y="1" w="1" h="1"> 18 | <jux text="Knobs:"/> 19 | <jux textA="Left Click/Drag/Wheel" textB="Modify Parameter Value"/> 20 | <jux textA="Hold Shift" textB="Sensitive Parameter Changes"/> 21 | <jux textA="Alt-/Double Click" textB="Back To Default Value"/> 22 | <jux textA="Right Click" textB="Context Menu"/> 23 | 24 | <jux text="Mod Dials:"/> 25 | <jux textA="Right Click" textB="Switch between Mod Depth and Bias"/> 26 | 27 | <jux text="Buttons:"/> 28 | <jux textA="Left Click/Wheel" textB="Modify Parameter Value"/> 29 | <jux textA="Alt-/Double Click" textB="Back To Default Value"/> 30 | 31 | <jux text="Text Fields:"/> 32 | <jux textA="Enter" textB="Apply Changes"/> 33 | <jux textA="Escape" textB="Discard Changes"/> 34 | <jux textA="Ctrl + C/V" textB="Copy / Paste"/> 35 | </juxtaposition> 36 | </menu> 37 | <menu id="Colours" x="1" y="1;5" tooltip="Individualize the colour sheme of the plugin here."> 38 | <title y="0" /> 39 | <colourscheme y="1" id="colourscheme"/> 40 | </menu> 41 | <menu id= 42 | "Manifest of Wisdom" x="1" y="1;13" tooltip="Use the manifest of wisdom to let your inspiration flow."> 43 | <title y="0" /> 44 | <erkenntnisse y="1" id="erkenntnisse"/> 45 | </menu> 46 | <menu id="Thank You" x="40;40;40" y="3;13;2" tooltip="I always wanted to become a VST Developer. Thanks to everyone who makes this possible."> 47 | <title y="0" w="2" /> 48 | <txt id="ThanksText" x=".1" y="1" w="2.8" h="1" just="topLeft" text= 49 | "Hi, 50 | 51 | my name is Florian Mrugalla and I am making beats and designing 52 | sound since I was a child. VST is a technology and trademark owned by 53 | Steinberg. But it's actually much more than that. VST plugins 54 | enable us to come together and engage in passionate discourse about 55 | art. I am very grateful for the opportunity to be part of this. 56 | 57 | That is why I consider VST a lifestyle! 58 | 59 | I always wanted to make my own VST plugins and here I am! 60 | Let's go on a journey together with my creations now. 61 | 62 | :)"/> 63 | <link id="Community" x="0" y="2" w="1.5" h="1" link="https://discord.gg/PFnAsvMW5f" tooltip="come to my discord to receive news or to tell me about bugs or feature requests!"/> 64 | <link id="Buy Me A Coffee" x="1.5" y="2" w="1.5" h="1" link="https://www.buymeacoffee.com/traderjoesR" tooltip="Consider a donation if you continuously find yourself enjoying the experience :>"/> 65 | </menu> 66 | </menu> -------------------------------------------------------------------------------- /Source/gui/Menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TextEditor.h" 3 | 4 | namespace gui 5 | { 6 | Just getJust(const String&); 7 | 8 | /// Sub Menus: 9 | 10 | struct ColourSelector : 11 | public Comp, 12 | public Timer 13 | { 14 | using CS = juce::ColourSelector; 15 | 16 | ColourSelector(Utils&); 17 | 18 | void paint(Graphics&) override; 19 | 20 | void resized() override; 21 | 22 | void timerCallback() override; 23 | 24 | protected: 25 | CS selector; 26 | Button revert, deflt; 27 | Colour curSheme; 28 | }; 29 | 30 | struct ErkenntnisseComp : 31 | public Comp, 32 | public Timer 33 | { 34 | ErkenntnisseComp(Utils&); 35 | 36 | void timerCallback() override; 37 | 38 | void resized() override; 39 | 40 | void paint(Graphics&) override; 41 | 42 | TextEditor editor; 43 | Label date; 44 | Button manifest, inspire, reveal, clear, paste; 45 | 46 | private: 47 | String getFolder(); 48 | 49 | void saveToDisk(); 50 | 51 | void parse(String&&); 52 | }; 53 | 54 | struct JuxtaComp : 55 | public Comp 56 | { 57 | struct Jux : 58 | public Comp 59 | { 60 | /* utils, text, textA, textB */ 61 | Jux(Utils&, String&&, String&&, String&&); 62 | 63 | void paint(Graphics&) override; 64 | 65 | void resized() override; 66 | 67 | protected: 68 | Label labelA, labelB; 69 | }; 70 | 71 | JuxtaComp(Utils&, const ValueTree&); 72 | 73 | void paint(Graphics&) override; 74 | 75 | void resized() override; 76 | 77 | std::vector<std::unique_ptr<Jux>> juxi; 78 | }; 79 | 80 | /// MENU STUFF IN GENERAL: 81 | 82 | struct ComponentWithBounds 83 | { 84 | /* comp, bounds, isQuad */ 85 | template<typename CompType> 86 | ComponentWithBounds(CompType* = nullptr, BoundsF&& = BoundsF(0.f, 0.f, 1.f, 1.f), bool = false); 87 | 88 | std::unique_ptr<Component> c; 89 | BoundsF b; 90 | bool isQuad; 91 | }; 92 | 93 | struct CompModular : 94 | public Comp 95 | { 96 | /* utils, tooltip, cursorType */ 97 | CompModular(Utils&, String&&, CursorType); 98 | 99 | void init(); 100 | 101 | std::vector<ComponentWithBounds> comps; 102 | protected: 103 | void paint(Graphics&) override; 104 | 105 | void resized() override; 106 | }; 107 | 108 | class NavBar : 109 | public Comp 110 | { 111 | struct Node 112 | { 113 | Node(const ValueTree&, int, int); 114 | 115 | const ValueTree vt; 116 | const int x, y; 117 | }; 118 | 119 | using Nodes = std::vector<Node>; 120 | 121 | Nodes makeNodes(const ValueTree&, int = 0, int = 0); 122 | 123 | int getDeepestNode() const noexcept; 124 | 125 | public: 126 | NavBar(Utils&, const ValueTree&); 127 | 128 | /* subMenu, parent */ 129 | void init(std::unique_ptr<CompModular>&, Comp&); 130 | 131 | protected: 132 | Label label; 133 | Nodes nodes; 134 | std::vector<std::unique_ptr<Button>> buttons; 135 | const int numMenus, deepestNode; 136 | 137 | void paint(Graphics&) override; 138 | 139 | void resized() override; 140 | }; 141 | 142 | struct Menu : 143 | public CompWidgetable 144 | { 145 | Menu(Utils&, const ValueTree&); 146 | 147 | protected: 148 | Label label; 149 | NavBar navBar; 150 | std::unique_ptr<CompModular> subMenu; 151 | 152 | void paint(juce::Graphics&) override; 153 | 154 | void resized() override; 155 | }; 156 | } -------------------------------------------------------------------------------- /Source/audio/Oscilloscope.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AudioUtils.h" 3 | #include "WHead.h" 4 | #include "Phasor.h" 5 | 6 | namespace audio 7 | { 8 | struct Oscilloscope 9 | { 10 | Oscilloscope() : 11 | wHead(), 12 | buffer(), 13 | phasor(0.), 14 | beatLength(1.f), 15 | Fs(0.) 16 | {} 17 | 18 | void prepare(double sampleRate, int blockSize) 19 | { 20 | Fs = sampleRate; 21 | 22 | auto windowSize = static_cast<int>(Fs) * 4; 23 | wHead.prepare(blockSize, windowSize); 24 | buffer.resize(windowSize); 25 | 26 | phasor.prepare(1. / Fs); 27 | } 28 | 29 | void operator()(const float** samples, int numChannels, int numSamples, 30 | const PlayHeadPos& playHead) noexcept 31 | { 32 | wHead(numSamples); 33 | 34 | const auto rateSyncV = 1.; 35 | 36 | const auto bpm = playHead.bpm; 37 | const auto bps = bpm * .0166666667; 38 | const auto quarterNoteLengthInSamples = Fs / bps; 39 | const auto barLengthInSamples = quarterNoteLengthInSamples * 4.; 40 | const auto beatLen = barLengthInSamples * rateSyncV; 41 | phasor.inc = 1. / beatLen; 42 | beatLength.store(static_cast<float>(beatLen)); 43 | 44 | const auto ppq = playHead.ppqPosition * .25; 45 | const auto ppqCh = ppq / rateSyncV; 46 | 47 | phasor.phase.phase = ppqCh - std::floor(ppqCh); 48 | 49 | for (auto s = 0; s < numSamples; ++s) 50 | { 51 | auto w = wHead[s]; 52 | 53 | const auto phaseInfo = phasor(); 54 | if (phaseInfo.retrig) 55 | { 56 | wHead.shift(-w, numSamples); 57 | w = wHead[s]; 58 | } 59 | 60 | buffer[w] = samples[0][s]; 61 | } 62 | 63 | if (numChannels == 2) 64 | { 65 | for (auto s = 0; s < numSamples; ++s) 66 | { 67 | const auto w = wHead[s]; 68 | buffer[w] = (buffer[w] + samples[1][s]) * .5f; 69 | } 70 | } 71 | } 72 | 73 | void operator()(const float* samples, int numSamples, 74 | const PlayHeadPos& playHead) noexcept 75 | { 76 | wHead(numSamples); 77 | 78 | const auto rateSyncV = 1.; 79 | 80 | const auto bpm = playHead.bpm; 81 | const auto bps = bpm * .0166666667; 82 | const auto quarterNoteLengthInSamples = Fs / bps; 83 | const auto barLengthInSamples = quarterNoteLengthInSamples * 4.; 84 | const auto beatLen = barLengthInSamples * rateSyncV; 85 | phasor.inc = 1. / beatLen; 86 | beatLength.store(static_cast<float>(beatLen)); 87 | 88 | const auto ppq = playHead.ppqPosition * .25; 89 | const auto ppqCh = ppq / rateSyncV; 90 | 91 | phasor.phase.phase = ppqCh - std::floor(ppqCh); 92 | 93 | for (auto s = 0; s < numSamples; ++s) 94 | { 95 | auto w = wHead[s]; 96 | 97 | const auto phaseInfo = phasor(); 98 | if (phaseInfo.retrig) 99 | { 100 | wHead.shift(-w, numSamples); 101 | w = wHead[s]; 102 | } 103 | 104 | buffer[w] = samples[s]; 105 | } 106 | } 107 | 108 | const float* data() const noexcept 109 | { 110 | return buffer.data(); 111 | } 112 | 113 | const size_t windowLength() const noexcept 114 | { 115 | return buffer.size(); 116 | } 117 | 118 | const float getBeatLength() const noexcept 119 | { 120 | return beatLength.load(); 121 | } 122 | protected: 123 | WHead wHead; 124 | std::vector<float> buffer; 125 | Phasor<double> phasor; 126 | std::atomic<float> beatLength; 127 | double Fs; 128 | }; 129 | } -------------------------------------------------------------------------------- /Source/gui/WaveTableDisplay.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../audio/WaveTable.h" 3 | #include "Button.h" 4 | 5 | namespace gui 6 | { 7 | template<size_t Size> 8 | struct WaveTableDisplay : 9 | public Button 10 | { 11 | enum class Mode { Wave, SpectralResponse, NumModes }; 12 | 13 | static constexpr float SizeF = static_cast<float>(Size); 14 | static constexpr float SizeInv = 1.f / SizeF; 15 | using WT = audio::WaveTable<Size>; 16 | 17 | Notify makeNotify(WaveTableDisplay& _wtd) 18 | { 19 | return [&wtd = _wtd](EvtType t, const void*) 20 | { 21 | if (t == EvtType::PatchUpdated) 22 | { 23 | wtd.repaint(); 24 | } 25 | else if (t == EvtType::FormulaUpdated) 26 | { 27 | wtd.repaint(); 28 | } 29 | }; 30 | } 31 | 32 | WaveTableDisplay(Utils& u, WT& _wt) : 33 | Button(u, "This Wavetable's display.", makeNotify(*this)), 34 | outlineCID(ColourID::Hover), 35 | lineCID(ColourID::Txt), 36 | mode(Mode::Wave), 37 | wt(_wt) 38 | { 39 | setBufferedToImage(true); 40 | 41 | //makeToggleButton(*this, ""); 42 | //onClick.push_back([&](Button&) 43 | //{ 44 | // mode = toggleState == 0 ? Mode::Wave : Mode::SpectralResponse; 45 | // repaint(); 46 | //}); 47 | //toggleState = 0; 48 | } 49 | 50 | void paint(Graphics& g) override 51 | { 52 | const auto thicc = utils.thicc; 53 | const auto bounds = getLocalBounds().toFloat().reduced(thicc); 54 | 55 | g.fillAll(Colours::c(ColourID::Darken)); 56 | 57 | g.setColour(Colours::c(outlineCID)); 58 | g.drawRoundedRectangle(bounds, thicc, thicc); 59 | g.setColour(Colours::c(lineCID)); 60 | 61 | const auto width = bounds.getWidth(); 62 | const auto height = bounds.getHeight(); 63 | 64 | if (mode == Mode::Wave) 65 | { 66 | const auto centreY = height * .5f; 67 | const auto cenY2 = bounds.getY() + centreY; 68 | const auto inc = width * SizeInv; 69 | auto x = bounds.getX(); 70 | for (auto s = 0; s < Size; ++s, x += inc) 71 | { 72 | auto y0 = cenY2; 73 | auto y1 = bounds.getY() + centreY * (1.f - wt(s)); 74 | if (y0 == y1) 75 | ++y1; 76 | else if (y0 > y1) 77 | std::swap(y0, y1); 78 | g.drawVerticalLine(static_cast<int>(x), y0, y1); 79 | } 80 | } 81 | else if (mode == Mode::SpectralResponse) 82 | { 83 | const auto wInt = static_cast<int>(width); 84 | const auto btm = bounds.getBottom(); 85 | 86 | for (auto i = 0; i < wInt; ++i) 87 | { 88 | const auto iF = static_cast<float>(i); 89 | const auto iTau = iF * Tau; 90 | 91 | auto mag = 0.f; 92 | for (auto s = 0; s < Size; ++s) 93 | { 94 | const auto sF = static_cast<float>(s); 95 | const auto phase = iTau * sF * SizeInv; 96 | const auto re = std::cos(phase); 97 | const auto im = std::sin(phase); 98 | const auto smpl = wt(s); 99 | const auto re2 = smpl * re; 100 | const auto im2 = smpl * im; 101 | mag += std::sqrt(re2 * re2 + im2 * im2); 102 | } 103 | mag = juce::jlimit(0.f, 1.f, mag * SizeInv); 104 | 105 | const auto x = static_cast<int>(bounds.getX() + iF); 106 | const auto y = height - height * mag; 107 | g.drawVerticalLine(x, y, btm); 108 | } 109 | } 110 | } 111 | 112 | ColourID outlineCID, lineCID; 113 | Mode mode; 114 | protected: 115 | WT& wt; 116 | }; 117 | } -------------------------------------------------------------------------------- /Source/arch/FormulaParser2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include <functional> 3 | #include "juce_core/juce_core.h" 4 | 5 | 6 | #define DebugFormularParser false 7 | 8 | namespace fx 9 | { 10 | using String = juce::String; 11 | using Func = std::function<float(float)>; 12 | using Func2 = std::function<float(float, float)>; 13 | using Char = juce::juce_wchar; 14 | using Random = juce::Random; 15 | 16 | bool isDigit(Char) noexcept; 17 | 18 | unsigned int getDigit(Char) noexcept; 19 | 20 | /* txt, idx */ 21 | template<typename Float> 22 | Float getNumber(const String&, int&) noexcept; 23 | 24 | enum class ParserErrorType 25 | { 26 | NoError, 27 | EmptyString, 28 | InvalidOperator, 29 | CouldNotTokenize, 30 | MismatchedParenthesis, 31 | UnknownToken, 32 | WroteAmountOfArguments, 33 | NumTypes 34 | }; 35 | 36 | String toString(ParserErrorType); 37 | 38 | enum class Operator 39 | { 40 | Plus, 41 | Minus, 42 | Multiply, 43 | Divide, 44 | Modulo, 45 | Power, 46 | Asinh, 47 | Acosh, 48 | Atanh, 49 | Floor, 50 | Log10, 51 | Noise, 52 | Asin, 53 | Acos, 54 | Atan, 55 | Ceil, 56 | Cosh, 57 | Log2, 58 | Sinh, 59 | Sign, 60 | Sqrt, 61 | Tanh, 62 | Abs, 63 | Cos, 64 | Exp, 65 | Sin, 66 | Tan, 67 | Log, 68 | Ln, 69 | NumOperators 70 | }; 71 | static constexpr int NumOperators = static_cast<int>(Operator::NumOperators); 72 | 73 | int getPrecedence(Operator); 74 | 75 | int getAssociativity(Operator); 76 | 77 | int getNumArguments(Operator) noexcept; 78 | 79 | Operator getOperator(const String&); 80 | 81 | String toString(Operator); 82 | 83 | /* txt, idx */ 84 | String getOperator(const String&, int&); 85 | 86 | Func getFunc(Operator); 87 | 88 | Func2 getFunc2(Operator); 89 | 90 | struct Token 91 | { 92 | enum class Type 93 | { 94 | Number, 95 | X, 96 | Operator, 97 | ParenthesisLeft, 98 | ParenthesisRight, 99 | NumTypes 100 | }; 101 | 102 | Token(Type, const String & = ""); 103 | 104 | Token(Type, const Char); 105 | 106 | Token(Operator); 107 | 108 | const Type type; 109 | const float value; 110 | const Operator op; 111 | const int precedence, associativity, numArguments; 112 | const Func func; 113 | const Func2 func2; 114 | }; 115 | 116 | String toString(const Token&); 117 | 118 | using Tokens = std::vector<Token>; 119 | 120 | String toString(const Tokens&); 121 | 122 | /* tokens, numbr */ 123 | void addNumberToTokens(Tokens&, float); 124 | 125 | Operator getRandomOperator(Random&) noexcept; 126 | 127 | /* tokens, rand, likelyX, numMin, numMax */ 128 | void addRandomNumber(Tokens&, Random&, float, float, float); 129 | 130 | /* postfix, numElements, likelyX, numMin, numMax */ 131 | void generateTerm(Tokens&, int, float, float, float); 132 | 133 | struct Parser 134 | { 135 | Parser(); 136 | 137 | bool operator()(const String&); 138 | 139 | /* postfix */ 140 | bool operator()(const Tokens&); 141 | 142 | /* x */ 143 | float operator()(float = 0.f) const noexcept; 144 | 145 | ParserErrorType errorType; 146 | protected: 147 | Func func; 148 | }; 149 | } 150 | 151 | // src: 152 | // https://www.youtube.com/watch?v=PAceaOSnxQs 153 | // https://stackoverflow.com/questions/789847/postfix-notation-validation 154 | // http://mathcenter.oxford.emory.edu/site/cs171/postfixExpressions/ 155 | 156 | #undef DebugFormularParser -------------------------------------------------------------------------------- /Source/audio/Meter.cpp: -------------------------------------------------------------------------------- 1 | #include "Meter.h" 2 | #include <cmath> 3 | 4 | namespace audio 5 | { 6 | // Meters::Val 7 | 8 | Meters::Val::Val() : 9 | rect(0.f), 10 | val(0.f), 11 | env(0.f), 12 | envFol() 13 | {}; 14 | 15 | // Meters 16 | 17 | Meters::Meters() : 18 | vals(), 19 | wHead(), 20 | lenInv(1.f), 21 | length(1) 22 | { 23 | } 24 | 25 | void Meters::prepare(float sampleRate, int blockSize) 26 | { 27 | length = static_cast<int>(sampleRate / PPDFPSMeters); 28 | wHead.prepare(blockSize, length); 29 | lenInv = 1.f / static_cast<float>(length); 30 | for (auto& v : vals) 31 | v.envFol.prepare(PPDFPSMeters); 32 | } 33 | 34 | #if PPDHasGainIn 35 | void Meters::processIn(const float* const* samples, int numChannels, int numSamples) noexcept 36 | { 37 | wHead(numSamples); 38 | 39 | process(vals[Type::In], samples, numChannels, numSamples); 40 | } 41 | #endif 42 | 43 | void Meters::processOut(const float* const* samples, int numChannels, int numSamples) noexcept 44 | { 45 | #if !PPDHasGainIn 46 | wHead(numSamples); 47 | #endif 48 | process(vals[Type::Out], samples, numChannels, numSamples); 49 | } 50 | 51 | const std::atomic<float>& Meters::operator()(int i) const noexcept 52 | { 53 | return vals[i].env; 54 | } 55 | 56 | void Meters::process(Val& val, const float* const* samples, int numChannels, int numSamples) noexcept 57 | { 58 | auto& rect = val.rect; 59 | auto& vVal = val.val; 60 | auto& envFol = val.envFol; 61 | 62 | #if PPDMetersUseRMS 63 | if (numChannels == 1) 64 | { 65 | auto smpls = samples[0]; 66 | 67 | 68 | for (auto s = 0; s < numSamples; ++s) 69 | { 70 | const auto w = wHead[s]; 71 | 72 | if (w == 0) 73 | { 74 | vVal = std::sqrt(rect * lenInv); 75 | val.env.store(envFol.process( 76 | vVal, 77 | RiseInMs, 78 | FallInMs 79 | )); 80 | 81 | rect = 0.f; 82 | } 83 | 84 | const auto smpl = smpls[s]; 85 | rect += smpl * smpl; 86 | } 87 | } 88 | else 89 | { 90 | for (auto s = 0; s < numSamples; ++s) 91 | { 92 | const auto w = wHead[s]; 93 | 94 | if (w == 0) 95 | { 96 | vVal = std::sqrt(rect * lenInv) * .5f; 97 | val.env.store(envFol.process( 98 | vVal, 99 | RiseInMs, 100 | FallInMs 101 | )); 102 | 103 | rect = 0.f; 104 | } 105 | 106 | const auto smpl = samples[0][s] + samples[1][s]; 107 | rect += smpl * smpl; 108 | } 109 | } 110 | #else 111 | if (numChannels == 1) 112 | { 113 | const auto smpls = samples[0]; 114 | 115 | for (auto s = 0; s < numSamples; ++s) 116 | { 117 | const auto w = wHead[s]; 118 | 119 | if (w == 0) 120 | { 121 | vVal = std::sqrt(rect); 122 | val.env.store(envFol.process( 123 | vVal, 124 | RiseInMs, 125 | FallInMs 126 | )); 127 | 128 | rect = 0.f; 129 | } 130 | 131 | const auto smpl = smpls[s]; 132 | rect = rect < smpl ? smpl : rect; 133 | } 134 | } 135 | else 136 | { 137 | for (auto s = 0; s < numSamples; ++s) 138 | { 139 | const auto w = wHead[s]; 140 | 141 | if (w == 0) 142 | { 143 | vVal = rect * .5f; 144 | val.env.store(envFol.process( 145 | vVal, 146 | RiseInMs, 147 | FallInMs 148 | )); 149 | 150 | rect = 0.f; 151 | } 152 | 153 | const auto smpl = samples[0][s] + samples[1][s]; 154 | rect = rect < smpl ? smpl : rect; 155 | } 156 | } 157 | #endif 158 | } 159 | } -------------------------------------------------------------------------------- /Source/gui/EQPad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GUIParams.h" 3 | #include <array> 4 | 5 | namespace gui 6 | { 7 | class EQPad : 8 | public Comp, 9 | public Timer 10 | { 11 | enum Tool { Select, Move, NumTools }; 12 | public: 13 | enum Dimension { X, Y, NumDimensions }; 14 | 15 | struct Node 16 | { 17 | /* u, xPID, yPID, scrollPID, rightClickPID, morePIDs */ 18 | Node(Utils&, PID, PID, PID, PID, const std::vector<PID>&); 19 | 20 | void paint(Graphics&) const; 21 | 22 | bool valueChanged() noexcept; 23 | 24 | float getValue(Dimension) const noexcept; 25 | 26 | void beginGesture() const; 27 | 28 | /* dof, speed */ 29 | void onDrag(const float*, float); 30 | 31 | /* wasDragged, altDown */ 32 | void endGesture(bool, bool); 33 | 34 | /* whellDeltaY, isReversed, shiftDown */ 35 | void onScroll(float, bool, bool); 36 | 37 | void onRightClick(); 38 | 39 | std::array<Param*, NumDimensions> xyParam; 40 | Param *scrollParam, *rightClickParam; 41 | std::vector<PID> morePIDs; 42 | BoundsF bounds; 43 | float x, y; 44 | protected: 45 | Utils& utils; 46 | }; 47 | 48 | using Nodes = std::vector<Node>; 49 | using NodePtrs = std::vector<Node*>; 50 | using OnSelectionChanged = std::function<void(const NodePtrs&)>; 51 | using OnSelectionChangeds = std::vector<OnSelectionChanged>; 52 | 53 | /* utils, tooltip */ 54 | EQPad(Utils&, String&&); 55 | 56 | /* xParam, yParam, scrollParam, rightClickParam, morePIDs */ 57 | void addNode(PID, PID, PID, PID, const std::vector<PID> & = {}); 58 | 59 | void selectNode(int); 60 | 61 | void selectAll(); 62 | 63 | void paint(Graphics&) override; 64 | 65 | void paintHovered(Graphics&); 66 | 67 | /* graphics, thicc */ 68 | void paintSelectionBounds(Graphics&, float); 69 | 70 | /* graphics, thicc */ 71 | void paintHighlightSelected(Graphics&, float); 72 | 73 | void resized() override; 74 | 75 | void timerCallback() override; 76 | 77 | size_t numNodes() noexcept; 78 | 79 | float getNodeSize() noexcept; 80 | 81 | void moveNode(int); 82 | 83 | Node* getNode(const PointF&) noexcept; 84 | 85 | bool alreadySelected(const Node&) const noexcept; 86 | 87 | bool notSelectedYet(const Node&) const noexcept; 88 | 89 | void mouseMove(const Mouse&) override; 90 | 91 | void mouseDown(const Mouse&) override; 92 | 93 | void mouseDrag(const Mouse&) override; 94 | 95 | void mouseUp(const Mouse&) override; 96 | 97 | void mouseWheelMove(const Mouse&, const MouseWheel&) override; 98 | 99 | void mouseDoubleClick(const Mouse&) override; 100 | 101 | void updateSelected(); 102 | 103 | void removeNode(const Node&); 104 | 105 | BoundsF bounds; 106 | OnSelectionChangeds onSelectionChanged; 107 | protected: 108 | Nodes nodes; 109 | NodePtrs selected; 110 | PointF dragXY; 111 | LineF selectionLine; 112 | BoundsF selectionBounds; 113 | Tool tool; 114 | Node* hovered; 115 | float hitBoxLength; 116 | 117 | void selectionChanged(); 118 | 119 | float normalizeX(float) const noexcept; 120 | 121 | float normalizeY(float) const noexcept; 122 | 123 | float denormalizeX(float) const noexcept; 124 | 125 | float denormalizeY(float) const noexcept; 126 | 127 | PointF normalize(PointF) const noexcept; 128 | 129 | PointF denormalize(PointF) const noexcept; 130 | 131 | BoundsF normalize(BoundsF) const; 132 | 133 | BoundsF denormalize(BoundsF) const; 134 | 135 | PointF limit(PointF) const noexcept; 136 | }; 137 | } -------------------------------------------------------------------------------- /Source/audio/PitchGlitcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "WHead.h" 3 | #include "PRM.h" 4 | #include "../arch/Interpolation.h" 5 | #include "AudioUtils.h" 6 | 7 | #include <array> 8 | 9 | #define PPDPitchShifterNumVoices 5 10 | 11 | namespace audio 12 | { 13 | class PitchGlitcher 14 | { 15 | static constexpr int NumVoices = PPDPitchShifterNumVoices; 16 | static constexpr float Inv12 = 1.f / 12.f; 17 | 18 | struct Phasor 19 | { 20 | Phasor(); 21 | 22 | /*blockSize*/ 23 | void prepare(int); 24 | 25 | /*numSamples*/ 26 | void operator()(int) noexcept; 27 | 28 | const float* data() const noexcept; 29 | 30 | float& operator[](int i) noexcept; 31 | const float& operator[](int i) const noexcept; 32 | 33 | std::vector<float> inc; 34 | protected: 35 | std::vector<float> buf; 36 | float phase; 37 | }; 38 | 39 | struct Window 40 | { 41 | static constexpr int TableSize = 1 << 13; 42 | 43 | Window(); 44 | 45 | /*blockSize*/ 46 | void prepare(int); 47 | 48 | /*phasor, numSamples*/ 49 | void operator()(const float*, int) noexcept; 50 | 51 | const float* data() const noexcept; 52 | 53 | protected: 54 | std::array<float, TableSize> table; 55 | std::vector<float> buf; 56 | float tableSizeF; 57 | }; 58 | 59 | struct ReadHead 60 | { 61 | ReadHead(); 62 | 63 | /*blockSize, sizeF*/ 64 | void prepare(int, float) noexcept; 65 | 66 | /*wHead, modulator, numSamples*/ 67 | void operator()(const int*, const float*, int) noexcept; 68 | 69 | const float* data() const noexcept; 70 | 71 | protected: 72 | std::vector<float> buf; 73 | float sizeF; 74 | }; 75 | 76 | struct Delay 77 | { 78 | Delay(); 79 | 80 | /* size */ 81 | void prepare(int); 82 | 83 | /* samples, numChannels, numSamples, wHead, readHead [0, size[, window, feedback[-1,1] */ 84 | void operator()(float* const*, int, int, const int*, const float*, const float*, float) noexcept; 85 | 86 | protected: 87 | std::array<std::vector<float>, 2> ringBuffer; 88 | 89 | float sizeF; 90 | int size; 91 | }; 92 | 93 | struct Shifter 94 | { 95 | Shifter(); 96 | 97 | /* Fs, blockSize, size */ 98 | void prepare(float, int, int); 99 | 100 | /* samples, numChannels, numSamples, wHead, grainBuf, tune, feedback */ 101 | void operator()(float* const*, int, int, const int*, const float*, float, float) noexcept; 102 | 103 | /* samples, numChannels, numSamples */ 104 | void copyTo(float* const*, int, int) noexcept; 105 | 106 | /* samples, numChannels, numSamples */ 107 | void addTo(float* const*, int, int) noexcept; 108 | 109 | protected: 110 | AudioBuffer audioBuffer; 111 | 112 | Phasor phasor; 113 | Window window; 114 | ReadHead readHead; 115 | Delay delay; 116 | 117 | PRM tuneParam; 118 | 119 | float sizeInv, Fs; 120 | }; 121 | 122 | public: 123 | PitchGlitcher(); 124 | 125 | /*Fs, blockSize*/ 126 | void prepare(float, int); 127 | 128 | /* samples, numChannels, numSamples, tuneP[-24,24], grainSizeP[0, sizeF], feedback[0,1], numVoicesP[1,NumVoices],spreadTuneP[0,1] */ 129 | void operator()(float* const*, int, int, float, float, float, int, float) noexcept; 130 | 131 | protected: 132 | WHead wHead; 133 | 134 | std::array<Shifter, NumVoices> shifter; 135 | 136 | PRM grainParam; 137 | 138 | float Fs; 139 | }; 140 | } 141 | 142 | #undef PPDPitchShifterNumVoices 143 | 144 | /* 145 | 146 | voice 1 need no copy to self buffer 147 | 148 | alter pitch per voice instead of grain size 149 | 150 | */ -------------------------------------------------------------------------------- /Source/gui/Comp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Utils.h" 3 | #include "Layout.h" 4 | #include <juce_gui_basics/juce_gui_basics.h> 5 | 6 | namespace gui 7 | { 8 | struct Comp : 9 | public Component 10 | { 11 | /* utils, tooltip, cursorType */ 12 | Comp(Utils&, const String& = "", CursorType = CursorType::Interact); 13 | 14 | /* utils, tooltip, notify, cursorType */ 15 | Comp(Utils&, const String&, Notify&&, CursorType = CursorType::Interact); 16 | 17 | const Utils& getUtils() const noexcept; 18 | Utils& getUtils() noexcept; 19 | 20 | const String* getTooltip() const noexcept; 21 | String* getTooltip() noexcept; 22 | 23 | void setTooltip(String&&); 24 | 25 | void setCursorType(CursorType); 26 | 27 | void updateCursor(); 28 | 29 | const Layout& getLayout() const noexcept; 30 | 31 | /* xL, yL */ 32 | void initLayout(const std::vector<int>&, const std::vector<int>&); 33 | 34 | /* xL, yL */ 35 | void initLayout(const String&, const String&); 36 | 37 | void notify(EvtType, const void* = nullptr); 38 | 39 | Utils& utils; 40 | Layout layout; 41 | String tooltip; 42 | protected: 43 | std::vector<Evt> evts; 44 | CursorType cursorType; 45 | 46 | void paint(Graphics&) override; 47 | 48 | void mouseEnter(const Mouse&) override; 49 | 50 | void mouseUp(const Mouse&) override; 51 | 52 | private: 53 | Notify makeNotifyBasic(Comp*); 54 | 55 | }; 56 | 57 | struct CompWidgetable : 58 | public Comp, 59 | public Timer 60 | { 61 | /* utils, tooltip, cursorType */ 62 | CompWidgetable(Utils&, String&& /*_tooltip*/, CursorType = CursorType::Interact); 63 | 64 | /* utils, tooltip, notify, cursorType */ 65 | CompWidgetable(Utils&, String&&, 66 | Notify&& = [](EvtType, const void*) {}, CursorType = CursorType::Interact); 67 | 68 | void defineBounds(const BoundsF&, const BoundsF&); 69 | 70 | /* lengthInSecs, widgetEnvelope */ 71 | void initWidget(float , bool = false); 72 | 73 | void updateBounds(); 74 | 75 | void timerCallback() override; 76 | 77 | BoundsF bounds0, bounds1; 78 | float widgetEnvelope; 79 | private: 80 | float widgetInc; 81 | }; 82 | 83 | struct CompScrollable : 84 | public Comp 85 | { 86 | struct ScrollBar : 87 | public Comp 88 | { 89 | static constexpr float SensitiveDrag = .2f; 90 | static constexpr float WheelDefaultSpeed = 12.f; 91 | 92 | /* utils, compScrollable, isVertical */ 93 | ScrollBar(Utils&, CompScrollable&, bool = true); 94 | 95 | bool needed() const noexcept; 96 | 97 | void mouseEnter(const Mouse&) override; 98 | 99 | void mouseDown(const Mouse&) override; 100 | 101 | void mouseDrag(const Mouse&) override; 102 | 103 | void mouseUp(const Mouse&) override; 104 | 105 | void mouseExit(const Mouse&) override; 106 | 107 | void mouseWheelMove(const Mouse&, const MouseWheel&) override; 108 | 109 | protected: 110 | CompScrollable& scrollable; 111 | float dragXY; 112 | bool vertical; 113 | 114 | void paint(Graphics&) override; 115 | 116 | void updateHandlePosY(float); 117 | 118 | void updateHandlePosX(float); 119 | }; 120 | 121 | /* utils, isVertical */ 122 | CompScrollable(Utils&, bool = true); 123 | 124 | void mouseWheelMove(const Mouse&, const MouseWheel&) override; 125 | 126 | protected: 127 | ScrollBar scrollBar; 128 | float xScrollOffset, yScrollOffset, actualHeight; 129 | }; 130 | 131 | struct CompScreenshotable : 132 | public Comp 133 | { 134 | using PPFunc = std::function<void(Graphics&, Image&)>; 135 | 136 | CompScreenshotable(Utils&); 137 | 138 | void resized() override; 139 | 140 | void paint(Graphics&) override; 141 | 142 | void takeScreenshot(); 143 | 144 | protected: 145 | Image screenshotImage; 146 | std::vector<PPFunc> onScreenshotFX; 147 | }; 148 | } -------------------------------------------------------------------------------- /Source/gui/Button.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Label.h" 3 | 4 | namespace gui 5 | { 6 | 7 | struct BlinkyBoy : 8 | public Timer 9 | { 10 | BlinkyBoy(); 11 | 12 | void init(Comp* _comp, float timeInSecs) noexcept; 13 | 14 | Colour getInterpolated(Colour c0, Colour c1) const noexcept; 15 | 16 | protected: 17 | Comp* comp; 18 | float env, inv; 19 | 20 | void timerCallback() override; 21 | }; 22 | 23 | struct Button : 24 | public Comp, 25 | public Timer 26 | { 27 | using OnClick = std::function<void(Button&, const Mouse&)>; 28 | using OnTimer = std::function<void(Button&)>; 29 | using OnPaint = std::function<void(Graphics&, Button&)>; 30 | using OnMouseWheel = std::function<void(Button&, const Mouse&, const MouseWheel&)>; 31 | 32 | void enableLabel(const String&); 33 | 34 | void enableLabel(const std::vector<String>&); 35 | 36 | void initLockButton(); 37 | 38 | /* pIDs */ 39 | void enableParameter(const std::vector<PID>&); 40 | 41 | /* utils, tooltip, notify */ 42 | Button(Utils&, String&& = "", Notify&& = [](EvtType, const void*){}); 43 | 44 | Label& getLabel() noexcept; 45 | 46 | const String& getText() const noexcept; 47 | 48 | std::vector<OnClick> onClick; 49 | std::vector<OnTimer> onTimer; 50 | std::vector<OnPaint> onPaint; 51 | std::vector<OnMouseWheel> onMouseWheel; 52 | BlinkyBoy blinkyBoy; 53 | int toggleState; 54 | std::vector<PID> pID; 55 | bool locked; 56 | int toggleNext; 57 | std::unique_ptr<Button> lockButton; 58 | Label label; 59 | std::vector<String> toggleTexts; 60 | 61 | void resized() override; 62 | 63 | void paint(Graphics&) override; 64 | 65 | void mouseEnter(const Mouse&) override; 66 | 67 | void mouseExit(const Mouse&) override; 68 | 69 | void mouseUp(const Mouse&) override; 70 | 71 | void mouseWheelMove(const Mouse&, const MouseWheel&) override; 72 | 73 | void timerCallback() override; 74 | }; 75 | 76 | Button::OnPaint buttonOnPaintDefault(); 77 | 78 | /* button; text/name; withToggle; targetToggleState */ 79 | void makeTextButton(Button&, const String&, bool = false, int = 1); 80 | 81 | /* button; toggletexts; withToggle; targetToggleState */ 82 | void makeTextButton(Button&, const std::vector<String>&, bool = false, int = 1); 83 | 84 | enum class ButtonSymbol 85 | { 86 | Empty, 87 | Polarity, 88 | StereoConfig, 89 | UnityGain, 90 | Power, 91 | PatchMode, 92 | Settings, 93 | Random, 94 | Abort, 95 | SwapParamModDepth, 96 | Lock, 97 | Save, 98 | Load, 99 | Remove, 100 | TuningFork, 101 | Lookahead, 102 | Img, 103 | Legato, 104 | TempoSync, 105 | InvertADSR, 106 | NumSymbols 107 | }; 108 | 109 | void paintAbort(Graphics&, BoundsF); 110 | 111 | /* button, symbol, targetToggleState */ 112 | void makeSymbolButton(Button&, ButtonSymbol, int = 1); 113 | 114 | /* button, text */ 115 | void makeToggleButton(Button&, const String&); 116 | 117 | /* button, pIDs, symbol, withToggle */ 118 | void makeParameter(Button&, const std::vector<PID>&, ButtonSymbol); 119 | 120 | /* button, pIDs, text, withToggle */ 121 | void makeParameter(Button&, const std::vector<PID>&, const String& = "", bool = false); 122 | 123 | /* button, pID, symbol, withToggle */ 124 | void makeParameter(Button&, PID, ButtonSymbol); 125 | 126 | /* button, pID, text, withToggle */ 127 | void makeParameter(Button&, PID, const String & = "", bool = false); 128 | 129 | template<size_t NumButtons> 130 | void makeParameterButtonsGroup(std::array<Button, NumButtons>&, PID, const char* /*txt*/, bool /*onlyText*/); 131 | 132 | void makeButtonsGroup(std::vector<std::unique_ptr<Button>>&, int /*defaultToggleStateIndex*/ = 0); 133 | 134 | void makeURLButton(Button&, String&& /*urlPath*/); 135 | } 136 | 137 | /* 138 | 139 | toggleState == 1 140 | has glow 141 | 142 | */ -------------------------------------------------------------------------------- /Source/gui/Knob.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GUIParams.h" 3 | #include "ContextMenu.h" 4 | #include <functional> 5 | 6 | namespace gui 7 | { 8 | struct Knob : 9 | public Comp, 10 | public Timer 11 | { 12 | using Func = std::function<void(Knob&)>; 13 | using OnDrag = std::function<void(Knob&, PointF&, bool)>; 14 | using OnUp = std::function<void(Knob&, const Mouse&)>; 15 | using OnTimer = std::function<bool(Knob&)>; 16 | using OnPaint = std::function<void(Knob&, Graphics&)>; 17 | using GetInfo = std::function<String(int)>; 18 | 19 | enum class DragMode { Vertical, Horizontal, Both, NumDragModes }; 20 | static constexpr int NumDragModes = static_cast<int>(DragMode::NumDragModes); 21 | 22 | /* utils, name, tooltip, cursorType */ 23 | Knob(Utils&, const String& = "", const String& = "", CursorType = CursorType::Interact); 24 | 25 | ~Knob(); 26 | 27 | void init(std::vector<int>&&, std::vector<int>&&); 28 | 29 | void timerCallback() override; 30 | 31 | void paint(juce::Graphics&) override; 32 | 33 | void resized() override; 34 | 35 | void mouseEnter(const Mouse&) override; 36 | 37 | void mouseExit(const Mouse&) override; 38 | 39 | void mouseDown(const Mouse&) override; 40 | 41 | void mouseDrag(const Mouse&) override; 42 | 43 | void mouseUp(const Mouse&) override; 44 | 45 | void mouseWheelMove(const Mouse&, const MouseWheel&) override; 46 | 47 | void mouseDoubleClick(const Mouse&) override; 48 | 49 | void setLocked(bool); 50 | 51 | Func onEnter, onExit, onDown, onWheel, onResize, onDoubleClick; 52 | OnDrag onDrag; 53 | OnUp onUp; 54 | OnTimer onTimer; 55 | OnPaint onPaint; 56 | GetInfo getInfo; 57 | Label label; 58 | PointF dragXY, lastPos; 59 | BoundsF knobBounds; 60 | std::vector<float> values; 61 | std::vector<std::unique_ptr<Comp>> comps; 62 | std::vector<int> states; 63 | bool hidesCursor, locked; 64 | DragMode dragMode; 65 | CursorType activeCursor; 66 | 67 | enum class LooksType 68 | { 69 | Default, 70 | VerticalSlider, 71 | HorizontalSlider, 72 | Knot, 73 | NumTypes 74 | }; 75 | }; 76 | 77 | bool isKnobLooksTypeModulatable(Knob::LooksType) noexcept; 78 | 79 | /* knob, name, tooltip, pseudo-parameter, looksType */ 80 | void makePseudoParameter(Knob&, const String&, String&&, std::atomic<float>*, Knob::LooksType = Knob::LooksType::Default); 81 | 82 | /* knob, pID, name, modulatable, meter, looksType */ 83 | void makeParameter(Knob&, PID, const String&, bool = true, const std::atomic<float>* = nullptr, Knob::LooksType = Knob::LooksType::Default); 84 | 85 | /* knob, pIDs, name, modulatable, meter, looksType */ 86 | void makeParameter(Knob&, const std::vector<PID>&, const String&, bool = true, const std::atomic<float>* = nullptr, Knob::LooksType = Knob::LooksType::Default); 87 | 88 | /* knob, pIDHorizontal, pIDVertical */ 89 | void makeParameter(Knob&, PID, PID); 90 | 91 | /* knob, pIDHorizontal, pIDVertical */ 92 | void makeParameter(Knob&, const std::vector<PID>&, PID); 93 | 94 | /* knob, pIDsHorizontal, pIDsVertical */ 95 | void makeParameter(Knob&, const std::vector<PID>&, const std::vector<PID>&); 96 | 97 | struct ContextMenuKnobs : 98 | public ContextMenu 99 | { 100 | Notify makeNotify2(ContextMenuKnobs&); 101 | 102 | ContextMenuKnobs(Utils&); 103 | }; 104 | 105 | struct TextEditorKnobs : 106 | public TextEditor 107 | { 108 | Notify makeNotify(TextEditorKnobs&); 109 | 110 | TextEditorKnobs(Utils&); 111 | 112 | void paint(Graphics&) override; 113 | }; 114 | 115 | } -------------------------------------------------------------------------------- /Source/audio/XenManager.cpp: -------------------------------------------------------------------------------- 1 | #include "XenManager.h" 2 | #include "../arch/Interpolation.h" 3 | #include "../arch/Conversion.h" 4 | 5 | namespace audio 6 | { 7 | // XenManager 8 | 9 | XenManager::XenManager() : 10 | xen(12.f), 11 | masterTune(440.f), 12 | baseNote(69.f), 13 | temperaments() 14 | { 15 | for (auto& t : temperaments) 16 | t = 0.f; 17 | } 18 | 19 | void XenManager::setTemperament(float tmprVal, int noteVal) noexcept 20 | { 21 | temperaments[noteVal] = tmprVal; 22 | const auto idx2 = noteVal + PPD_MaxXen; 23 | if (idx2 >= temperaments.size()) 24 | temperaments[idx2] = tmprVal; 25 | } 26 | 27 | void XenManager::operator()(float _xen, float _masterTune, float _baseNote) noexcept 28 | { 29 | xen = _xen; 30 | masterTune = _masterTune; 31 | baseNote = _baseNote; 32 | } 33 | 34 | template<typename Float> 35 | Float XenManager::noteToFreqHz(Float note) const noexcept 36 | { 37 | const auto noteCap = juce::jlimit(static_cast<Float>(0), static_cast<Float>(PPD_MaxXen), note); 38 | const auto tmprmt = static_cast<Float>(temperaments[static_cast<int>(std::round(noteCap))].load()); 39 | 40 | return noteInFreqHz(note + tmprmt, static_cast<Float>(baseNote), static_cast<Float>(xen), static_cast<Float>(masterTune)); 41 | } 42 | 43 | template<typename Float> 44 | Float XenManager::noteToFreqHzWithWrap(Float note, Float lowestFreq, Float highestFreq) const noexcept 45 | { 46 | auto freq = noteToFreqHz(note); 47 | while (freq < lowestFreq) 48 | freq *= static_cast<Float>(2); 49 | while (freq >= highestFreq) 50 | freq *= static_cast<Float>(.5); 51 | return freq; 52 | } 53 | 54 | template<typename Float> 55 | Float XenManager::freqHzToNote(Float hz) noexcept 56 | { 57 | return freqHzInNote(hz, static_cast<Float>(baseNote), static_cast<Float>(xen), static_cast<Float>(masterTune)); 58 | } 59 | 60 | float XenManager::getXen() const noexcept 61 | { 62 | return xen; 63 | } 64 | 65 | template float XenManager::noteToFreqHz<float>(float note) const noexcept; 66 | template double XenManager::noteToFreqHz<double>(double note) const noexcept; 67 | 68 | template float XenManager::noteToFreqHzWithWrap<float>(float note, float lowestFreq, float highestFreq) const noexcept; 69 | template double XenManager::noteToFreqHzWithWrap<double>(double note, double lowestFreq, double highestFreq) const noexcept; 70 | 71 | template float XenManager::freqHzToNote<float>(float hz) noexcept; 72 | template double XenManager::freqHzToNote<double>(double hz) noexcept; 73 | 74 | // TuningEditorSynth 75 | 76 | TuningEditorSynth::TuningEditorSynth(const XenManager& _xen) : 77 | pitch(69.f), 78 | gain(.25f), 79 | noteOn(false), 80 | 81 | xen(_xen), 82 | osc(), 83 | buffer() 84 | { 85 | 86 | } 87 | 88 | void TuningEditorSynth::loadPatch(sta::State& state) 89 | { 90 | const auto idStr = "tuningEditor"; 91 | auto g = state.get(idStr, "gain"); 92 | if (g != nullptr) 93 | gain.store(static_cast<float>(*g)); 94 | } 95 | 96 | void TuningEditorSynth::savePatch(sta::State& state) 97 | { 98 | const auto idStr = "tuningEditor"; 99 | state.set(idStr, "gain", gain.load()); 100 | } 101 | 102 | void TuningEditorSynth::prepare(float Fs, int blockSize) 103 | { 104 | const auto fsInv = 1.f / Fs; 105 | osc.prepare(fsInv); 106 | 107 | buffer.resize(blockSize, 0.f); 108 | } 109 | 110 | void TuningEditorSynth::operator()(float* const* samples, int numChannels, int numSamples) noexcept 111 | { 112 | if (noteOn.load()) 113 | { 114 | auto buf = buffer.data(); 115 | 116 | auto g = gain.load(); 117 | 118 | const auto freqHz = xen.noteToFreqHzWithWrap(pitch.load()); 119 | osc.setFreqHz(freqHz); 120 | 121 | for (auto s = 0; s < numSamples; ++s) 122 | buf[s] = std::tanh(4.f * osc()) * g; 123 | 124 | for (auto ch = 0; ch < numChannels; ++ch) 125 | SIMD::add(samples[ch], buf, numSamples); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /Source/audio/AutoGain.cpp: -------------------------------------------------------------------------------- 1 | #include "AutoGain.h" 2 | #include "AudioUtils.h" 3 | #include <random> 4 | 5 | namespace audio 6 | { 7 | using MersenneTwister = std::mt19937; 8 | using RandDistribution = std::uniform_real_distribution<float>; 9 | 10 | // PinkNoise 11 | 12 | PinkNoise::PinkNoise(float targetDb) : 13 | noise() 14 | { 15 | synthesizeWhiteNoise(); 16 | pinkenNoise(); 17 | 18 | const auto targetRms = Decibels::decibelsToGain(targetDb); 19 | const auto gain = targetRms / rms(); 20 | 21 | for (auto& n : noise) 22 | n *= gain; 23 | } 24 | 25 | float PinkNoise::rms() noexcept 26 | { 27 | return getRMS(noise.data(), Size); 28 | } 29 | 30 | float* PinkNoise::data() noexcept 31 | { 32 | return noise.data(); 33 | } 34 | 35 | const float* PinkNoise::data() const noexcept 36 | { 37 | return noise.data(); 38 | } 39 | 40 | void PinkNoise::synthesizeWhiteNoise() noexcept 41 | { 42 | { // PROCEDURAL WHITE NOISE 43 | MersenneTwister mt(420); 44 | RandDistribution dist(-1.f, 1.f); 45 | 46 | for (auto n = 0; n < Size; ++n) 47 | noise[n] = dist(mt); 48 | } 49 | } 50 | 51 | void PinkNoise::pinkenNoise() noexcept 52 | { 53 | { // WHITE >> PINK NOISE 54 | std::array<float, 7> b; 55 | for (auto& a : b) 56 | a = 0.f; 57 | for (auto n = 0; n < Size; ++n) 58 | { 59 | const auto white = noise[n]; 60 | b[0] = 0.99886f * b[0] + white * 0.0555179f; 61 | b[1] = 0.99332f * b[1] + white * 0.0750759f; 62 | b[2] = 0.96900f * b[2] + white * 0.1538520f; 63 | b[3] = 0.86650f * b[3] + white * 0.3104856f; 64 | b[4] = 0.55000f * b[4] + white * 0.5329522f; 65 | b[5] = -0.7616f * b[5] - white * 0.0168980f; 66 | auto pink = white * 0.5362f; 67 | for (auto a : b) 68 | pink += a; 69 | b[6] = white * 0.115926f; 70 | noise[n] = pink; 71 | } 72 | } 73 | } 74 | 75 | // AutoGain 76 | 77 | AutoGain::AutoGain(PinkNoise& _noise, const Range& _range, int _numGainSteps) : 78 | noise(_noise), 79 | range(_range), 80 | gain(), 81 | numGainStepsF(static_cast<float>(_numGainSteps)), 82 | numGainSteps(_numGainSteps), 83 | evaluating(true) 84 | { 85 | gain.resize(numGainSteps + 2, 1.f); 86 | } 87 | 88 | void AutoGain::evaluate(const OnPrepare& onPrepare, 89 | const OnProcess& onProcess, const OnClear& onClear) 90 | { 91 | onPrepare(44100.f, noise.Size); 92 | const auto noiseRMS = noise.rms(); 93 | AudioBuffer buf(1, noise.Size); 94 | for (auto i = 0; i < gain.size(); ++i) 95 | { 96 | auto samples = buf.getArrayOfWritePointers(); 97 | SIMD::copy(samples[0], noise.data(), noise.Size); 98 | 99 | auto x = static_cast<float>(i) / numGainStepsF; 100 | x = x > 1.f ? 1.f : x; 101 | const auto pVal = range.convertFrom0to1(x); 102 | 103 | onProcess(samples, 1, noise.Size, pVal); 104 | 105 | const auto samplesRead = samples[0]; 106 | const auto nRMS = getRMS(samplesRead, noise.Size); 107 | gain[i] = (nRMS == 0.f || std::isnan(nRMS) || std::isinf(nRMS)) ? 0.f : noiseRMS / nRMS; 108 | } 109 | onClear(); 110 | evaluating = false; 111 | } 112 | 113 | float AutoGain::fromDenorm(float smpl, float valPDenorm) const noexcept 114 | { 115 | if (evaluating) 116 | return smpl; 117 | 118 | return processSample(smpl, range.convertTo0to1(valPDenorm)); 119 | } 120 | 121 | float AutoGain::operator()(float smpl, float valP) const noexcept 122 | { 123 | if (evaluating) 124 | return smpl; 125 | 126 | return processSample(smpl, valP); 127 | } 128 | 129 | float AutoGain::processSample(float smpl, float valP) const noexcept 130 | { 131 | const auto mcr = valP; 132 | const auto x = mcr * numGainStepsF; 133 | const auto xFloor = std::floor(x); 134 | const auto iF = static_cast<int>(xFloor); 135 | const auto iC = iF + 1; 136 | const auto xFrac = x - xFloor; 137 | const auto gF = gain[iF]; 138 | const auto gC = gain[iC]; 139 | const auto gRange = gC - gF; 140 | const auto g = gF + xFrac * gRange; 141 | smpl *= g; 142 | 143 | return smpl; 144 | } 145 | } -------------------------------------------------------------------------------- /Source/Processor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include <juce_audio_processors/juce_audio_processors.h> 4 | #include <juce_events/juce_events.h> 5 | 6 | #include "audio/XenManager.h" 7 | #include "audio/MIDIManager.h" 8 | #include "audio/MIDILearn.h" 9 | #include "audio/ProcessSuspend.h" 10 | #include "audio/DryWetMix.h" 11 | #if PPDHasStereoConfig 12 | #include "audio/MidSide.h" 13 | #endif 14 | #include "audio/Oversampling.h" 15 | #include "audio/Meter.h" 16 | 17 | #include "audio/PRM.h" 18 | #include "audio/AudioUtils.h" 19 | 20 | #include "audio/Filter.h" 21 | 22 | namespace audio 23 | { 24 | using MacroProcessor = param::MacroProcessor; 25 | using Timer = juce::Timer; 26 | 27 | struct ProcessorBackEnd : 28 | public juce::AudioProcessor, 29 | public Timer 30 | { 31 | using ChannelSet = juce::AudioChannelSet; 32 | using AppProps = juce::ApplicationProperties; 33 | 34 | ProcessorBackEnd(); 35 | ~ProcessorBackEnd(); 36 | 37 | const String getName() const override; 38 | double getTailLengthSeconds() const override; 39 | int getNumPrograms() override; 40 | int getCurrentProgram() override; 41 | void setCurrentProgram(int) override; 42 | const String getProgramName(int) override; 43 | void changeProgramName(int, const String&) override; 44 | bool isBusesLayoutSupported(const BusesLayout&) const override; 45 | AppProps* getProps() noexcept; 46 | bool canAddBus(bool) const override; 47 | 48 | void savePatch(); 49 | void loadPatch(); 50 | 51 | bool hasEditor() const override; 52 | bool acceptsMidi() const override; 53 | bool producesMidi() const override; 54 | bool isMidiEffect() const override; 55 | 56 | juce::AudioProcessor::BusesProperties makeBusesProperties(); 57 | 58 | PlayHeadPos playHeadPos; 59 | AppProps props; 60 | ProcessSuspender sus; 61 | 62 | XenManager xenManager; 63 | State state; 64 | Params params; 65 | MacroProcessor macroProcessor; 66 | MIDIManager midiManager; 67 | DryWetMix dryWetMix; 68 | #if PPDHasHQ 69 | Oversampler oversampler; 70 | #endif 71 | Meters meters; 72 | MIDIVoices midiVoices; 73 | #if PPDHasTuningEditor 74 | TuningEditorSynth tuningEditorSynth; 75 | #endif 76 | 77 | void forcePrepareToPlay(); 78 | 79 | void timerCallback() override; 80 | 81 | void processBlockBypassed(AudioBuffer&, juce::MidiBuffer&) override; 82 | 83 | #if PPDHasStereoConfig 84 | bool midSideEnabled; 85 | #endif 86 | #if PPDHasLookahead 87 | bool lookaheadEnabled; 88 | #endif 89 | 90 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProcessorBackEnd) 91 | }; 92 | 93 | struct Processor : 94 | public ProcessorBackEnd 95 | { 96 | Processor(); 97 | 98 | void prepareToPlay(double, int) override; 99 | 100 | void processBlock(AudioBuffer&, juce::MidiBuffer&) override; 101 | 102 | void processBlockBypassed(AudioBuffer&, juce::MidiBuffer&) override; 103 | 104 | /* samples, numChannels, numSamples, midi, samplesSC, numChannelsSC */ 105 | void processBlockPreUpscaled(float* const*, int numChannels, int numSamples, juce::MidiBuffer& midi) noexcept; 106 | 107 | /* samples, numChannels, numSamples, samplesSC, numChannelsSC */ 108 | void processBlockUpsampled(float* const*, int, int 109 | #if PPDHasSidechain 110 | , float**, int 111 | #endif 112 | ) noexcept; 113 | 114 | void releaseResources() override; 115 | 116 | ///////////////////////////////////////////// 117 | ///////////////////////////////////////////// 118 | void getStateInformation(juce::MemoryBlock&) override; 119 | /* data, sizeInBytes */ 120 | void setStateInformation(const void*, int) override; 121 | 122 | void savePatch(); 123 | 124 | void loadPatch(); 125 | 126 | juce::AudioProcessorEditor* createEditor() override; 127 | 128 | std::vector<IIR> filter; 129 | PRM cutoffSmooth, qSmooth; 130 | }; 131 | } -------------------------------------------------------------------------------- /Source/audio/CombFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "CombFilter.h" 2 | #include "../arch/Interpolation.h" 3 | 4 | namespace audio 5 | { 6 | // CombFilter::DelayFeedback 7 | 8 | CombFilter::DelayFeedback::DelayFeedback() : 9 | ringBuffer(), 10 | lowpass{ 0.f, 0.f }, 11 | size(0) 12 | {} 13 | 14 | void CombFilter::DelayFeedback::prepare(float Fs, int _size) 15 | { 16 | size = _size; 17 | 18 | ringBuffer.setSize(2, size, false, true, false); 19 | 20 | for (auto& lp : lowpass) 21 | lp.makeFromDecayInHz(1000.f, Fs); 22 | } 23 | 24 | void CombFilter::DelayFeedback::operator()(float* const* samples, int numChannels, int numSamples, 25 | const int* wHead, const float* fbBuf, const float* dampBuf, 26 | const float* const* readHead) noexcept 27 | { 28 | auto ringBuf = ringBuffer.getArrayOfWritePointers(); 29 | 30 | for (auto ch = 0; ch < numChannels; ++ch) 31 | { 32 | auto smpls = samples[ch]; 33 | auto ring = ringBuf[ch]; 34 | const auto rHead = readHead[ch]; 35 | auto& lp = lowpass[ch]; 36 | 37 | for (auto s = 0; s < numSamples; ++s) 38 | { 39 | lp.setX(dampBuf[s]); 40 | 41 | const auto w = wHead[s]; 42 | const auto r = rHead[s]; 43 | const auto fb = fbBuf[s]; 44 | 45 | const auto sOut = lp(interpolate::cubicHermiteSpline(ring, r, size)) * fb + smpls[s]; 46 | const auto sIn = sOut; 47 | 48 | ring[w] = sIn; 49 | smpls[s] = sOut; 50 | } 51 | } 52 | } 53 | 54 | // CombFilter 55 | 56 | CombFilter::CombFilter(MIDIVoices& _midiVoices, const XenManager& _xenManager) : 57 | midiVoices(_midiVoices), 58 | xenManager(_xenManager), 59 | 60 | writeHead(), 61 | readHeadBuffer(), 62 | delay(), 63 | 64 | feedbackP(0.f), 65 | dampP(1.f), 66 | retuneP(0.f), 67 | 68 | Fs(0.f), sizeF(0.f), curDelay(0.f), curNote(48.f), 69 | size(0) 70 | {} 71 | 72 | void CombFilter::prepare(float sampleRate, int blockSize) 73 | { 74 | Fs = sampleRate; 75 | 76 | sizeF = std::ceil(freqHzInSamples(LowestFrequencyHz, Fs)); 77 | size = static_cast<int>(sizeF); 78 | 79 | writeHead.prepare(blockSize, size); 80 | readHeadBuffer.setSize(2, blockSize, false, false, false); 81 | delay.prepare(Fs, size); 82 | 83 | const auto freqHz = xenManager.noteToFreqHzWithWrap(curNote, LowestFrequencyHz); 84 | curDelay = freqHzInSamples(freqHz, Fs); 85 | 86 | feedbackP.prepare(sampleRate, blockSize, 10.f); 87 | dampP.prepare(sampleRate, blockSize, 10.f); 88 | retuneP.prepare(sampleRate, blockSize, 10.f); 89 | } 90 | 91 | void CombFilter::operator()(float* const* samples, int numChannels, int numSamples, 92 | float _feedback, float _damp, float _retune) noexcept 93 | { 94 | writeHead(numSamples); 95 | const auto wHead = writeHead.data(); 96 | 97 | const auto retuneBuf = retuneP(_retune, numSamples); 98 | 99 | { // calculate readhead indexes from note buffer 100 | auto rHeadBuf = readHeadBuffer.getArrayOfWritePointers(); 101 | 102 | auto& noteBuffer = midiVoices.voices[0].buffer; 103 | 104 | for (auto s = 0; s < numSamples; ++s) 105 | { 106 | auto nNote = static_cast<float>(noteBuffer[0].noteNumber); 107 | const auto pb = midiVoices.pitchbendBuffer.buffer[s]; 108 | 109 | nNote = juce::jlimit(1.f, 127.f, nNote + retuneBuf[s] + pb); 110 | 111 | if (curNote != nNote) 112 | { 113 | curNote = nNote; 114 | const auto freqHz = xenManager.noteToFreqHzWithWrap(curNote, LowestFrequencyHz); 115 | curDelay = freqHzInSamples(freqHz, Fs); 116 | } 117 | 118 | const auto w = static_cast<float>(wHead[s]); 119 | auto r = w - curDelay; 120 | if (r < 0.f) 121 | r += sizeF; 122 | 123 | rHeadBuf[0][s] = r; 124 | } 125 | 126 | for (auto ch = 1; ch < numChannels; ++ch) // only until i have a better idea 127 | SIMD::copy(rHeadBuf[ch], rHeadBuf[0], numSamples); 128 | } 129 | 130 | const auto fbBuf = feedbackP(_feedback, numSamples); 131 | const auto rHeadBufConst = readHeadBuffer.getArrayOfReadPointers(); 132 | 133 | const auto xFromHz = smooth::Lowpass<float>::getXFromHz(_damp, Fs); 134 | const auto dampBuf = dampP(xFromHz, numSamples); 135 | 136 | delay(samples, numChannels, numSamples, 137 | wHead, fbBuf, dampBuf, rHeadBufConst); 138 | } 139 | } -------------------------------------------------------------------------------- /Source/gui/Shared.cpp: -------------------------------------------------------------------------------- 1 | #include "Shared.h" 2 | 3 | namespace gui 4 | { 5 | Colours Colours::c{}; 6 | 7 | Font getFont(const char* ttf, size_t size) 8 | { 9 | auto typeface = juce::Typeface::createSystemTypefaceFor(ttf, size); 10 | return Font(typeface); 11 | } 12 | 13 | Font getFontNEL() 14 | { 15 | return getFont(BinaryData::nel19_ttf, BinaryData::nel19_ttfSize); 16 | } 17 | Font getFontLobster() 18 | { 19 | return getFont(BinaryData::LobsterRegular_ttf, BinaryData::LobsterRegular_ttfSize); 20 | } 21 | Font getFontMsMadi() 22 | { 23 | return getFont(BinaryData::MsMadiRegular_ttf, BinaryData::MsMadiRegular_ttfSize); 24 | } 25 | Font getFontDosisSemiBold() 26 | { 27 | return getFont(BinaryData::DosisSemiBold_ttf, BinaryData::DosisSemiBold_ttfSize); 28 | } 29 | Font getFontDosisBold() 30 | { 31 | return getFont(BinaryData::DosisBold_ttf, BinaryData::DosisBold_ttfSize); 32 | } 33 | Font getFontDosisExtraBold() 34 | { 35 | return getFont(BinaryData::DosisExtraBold_ttf, BinaryData::DosisExtraBold_ttfSize); 36 | } 37 | Font getFontDosisLight() 38 | { 39 | return getFont(BinaryData::DosisLight_ttf, BinaryData::DosisLight_ttfSize); 40 | } 41 | Font getFontDosisExtraLight() 42 | { 43 | return getFont(BinaryData::DosisExtraLight_ttf, BinaryData::DosisExtraLight_ttfSize); 44 | } 45 | Font getFontDosisMedium() 46 | { 47 | return getFont(BinaryData::DosisMedium_ttf, BinaryData::DosisMedium_ttfSize); 48 | } 49 | Font getFontDosisRegular() 50 | { 51 | return getFont(BinaryData::DosisRegular_ttf, BinaryData::DosisRegular_ttfSize); 52 | } 53 | Font getFontDosisVariable() 54 | { 55 | return getFont(BinaryData::DosisVariableFont_wght_ttf, BinaryData::DosisVariableFont_wght_ttfSize); 56 | } 57 | 58 | // Colours 59 | 60 | Colours::Colours() : 61 | cols(), 62 | props(nullptr) 63 | { 64 | setInternal(ColourID::Transp, Colour(0x00000000)); 65 | setInternal(ColourID::Abort, Colour(0xffff0000)); 66 | } 67 | 68 | Colour Colours::defaultColour() noexcept 69 | { 70 | return Colour(0xffa8e753); 71 | } 72 | 73 | void Colours::init(Props* p) 74 | { 75 | props = p; 76 | if (props->isValidFile()) 77 | { 78 | const auto colStr = props->getValue(coloursID(), defaultColour().toString()); 79 | set(juce::Colour::fromString(colStr)); 80 | } 81 | } 82 | 83 | bool Colours::set(Colour col) 84 | { 85 | if (props->isValidFile()) 86 | { 87 | setInternal(ColourID::Interact, col); 88 | props->setValue(coloursID(), col.toString()); 89 | 90 | setInternal(ColourID::Bg, col.darker(8.f).withMultipliedSaturation(.15f)); 91 | setInternal(ColourID::Txt, col.withRotatedHue(-1.f / 9.f).withMultipliedBrightness(2.f)); 92 | setInternal(ColourID::Mod, col.withRotatedHue(1.f / 3.f)); 93 | setInternal(ColourID::Bias, col.withRotatedHue(.5f)); 94 | setInternal(ColourID::Darken, col.darker(3.f).withMultipliedAlpha(.3f)); 95 | setInternal(ColourID::Hover, col.withMultipliedSaturation(2.f).brighter(2.f).withMultipliedAlpha(.3f)); 96 | setInternal(ColourID::Inactive, col.withMultipliedSaturation(.1f)); 97 | 98 | if (props->needsToBeSaved()) 99 | { 100 | props->save(); 101 | props->sendChangeMessage(); 102 | return true; 103 | } 104 | } 105 | return false; 106 | } 107 | 108 | Colour Colours::operator()(ColourID i) const noexcept 109 | { 110 | return get(static_cast<int>(i)); 111 | } 112 | 113 | Colour Colours::operator()(int i) const noexcept 114 | { 115 | return get(i); 116 | } 117 | 118 | Colour Colours::get(int i) const noexcept 119 | { 120 | return cols[i]; 121 | } 122 | 123 | void Colours::setInternal(ColourID cID, Colour col) noexcept 124 | { 125 | cols[static_cast<int>(cID)] = col; 126 | } 127 | 128 | String Colours::coloursID() 129 | { 130 | return "coloursMain"; 131 | } 132 | } -------------------------------------------------------------------------------- /Source/gui/Layout.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Using.h" 3 | 4 | namespace gui 5 | { 6 | BoundsF smallestBoundsIn(const LineF& line) noexcept; 7 | 8 | BoundsF maxQuadIn(const BoundsF&) noexcept; 9 | 10 | void repaintWithChildren(Component*); 11 | 12 | std::unique_ptr<juce::XmlElement> loadXML(const char*, const int); 13 | 14 | class Layout 15 | { 16 | public: 17 | Layout(const Component&); 18 | 19 | void init(const std::vector<int>& /*xDist*/, const std::vector<int>& /*yDist*/); 20 | 21 | void fromStrings(const String& /*xDist*/, const String& /*yDist*/); 22 | 23 | void resized() noexcept; 24 | 25 | template<typename X, typename Y> 26 | PointF operator()(X, Y) const noexcept; 27 | 28 | template<typename PointType> 29 | PointF operator()(PointType) const noexcept; 30 | 31 | template<typename X, typename Y> 32 | BoundsF operator()(X, Y, X, Y, bool /*isQuad*/ = false) const noexcept; 33 | 34 | template<typename PointType0, typename PointType1> 35 | LineF getLine(PointType0, PointType1) const noexcept; 36 | 37 | template<typename X0, typename Y0, typename X1, typename Y1> 38 | LineF getLine(X0, Y0, X1, Y1) const noexcept; 39 | 40 | BoundsF bottom(bool /*isQuad*/ = false) const noexcept; 41 | 42 | BoundsF top(bool /*isQuad*/ = false) const noexcept; 43 | 44 | BoundsF right(bool /*isQuad*/ = false) const noexcept; 45 | 46 | float getX(int) const noexcept; 47 | float getY(int) const noexcept; 48 | float getX(float) const noexcept; 49 | 50 | float getY(float) const noexcept; 51 | 52 | template<typename X> 53 | float getW(X) const noexcept; 54 | 55 | template<typename Y> 56 | float getH(Y) const noexcept; 57 | 58 | template<typename X, typename Y> 59 | void place(Component&, X, Y y, X = static_cast<X>(1), Y = static_cast<Y>(1), bool /*isQuad*/ = false) const noexcept; 60 | 61 | template<typename X, typename Y> 62 | void place(Component*, X, Y, X = static_cast<X>(1), Y = static_cast<Y>(1), bool /*isQuad*/ = false) const noexcept; 63 | 64 | void paint(Graphics&); 65 | 66 | template<typename X, typename Y> 67 | void label(Graphics&, String&&, X, Y, X = static_cast<X>(1), Y = static_cast<Y>(1), bool /*isQuad*/ = false) const; 68 | 69 | protected: 70 | const Component& comp; 71 | std::vector<float> rXRaw, rX; 72 | std::vector<float> rYRaw, rY; 73 | }; 74 | 75 | void make(Path&, const Layout&, std::vector<Point>&&); 76 | 77 | void drawHorizontalLine(Graphics&, int /*y*/, float /*left*/, float /*right*/, int /*thicc*/ = 1); 78 | 79 | void drawVerticalLine(Graphics&, int /*x*/, float /*top*/, float /*bottom*/, int /*thicc*/ = 1); 80 | 81 | /* graphics, bounds, edgeWidth, edgeHeight, stroke */ 82 | void drawRectEdges(Graphics&, const BoundsF&, float, float, const Stroke&); 83 | 84 | /* graphics, bounds, edgeWidth, stroke */ 85 | void drawRectEdges(Graphics&, const BoundsF&, float, const Stroke&); 86 | 87 | /* graphics, bounds, text */ 88 | void drawHeadLine(Graphics&, const BoundsF&, const String&); 89 | 90 | template<class ArrayCompPtr> 91 | inline void distributeVertically(const Component& parent, ArrayCompPtr& compArray) 92 | { 93 | const auto w = static_cast<float>(parent.getWidth()); 94 | const auto h = static_cast<float>(parent.getHeight()); 95 | const auto x = 0.f; 96 | 97 | auto y = 0.f; 98 | 99 | const auto cH = h / static_cast<float>(compArray.size()); 100 | 101 | for (auto& cmp : compArray) 102 | { 103 | cmp->setBounds(BoundsF(x, y, w, cH).toNearestInt()); 104 | y += cH; 105 | } 106 | } 107 | 108 | template<class SizeType> 109 | inline void distributeVertically(const Component& parent, Component* compArray, SizeType size) 110 | { 111 | const auto w = static_cast<float>(parent.getWidth()); 112 | const auto h = static_cast<float>(parent.getHeight()); 113 | const auto x = 0.f; 114 | 115 | auto y = 0.f; 116 | 117 | const auto cH = h / static_cast<float>(size); 118 | 119 | for (auto i = 0; i < size; ++i) 120 | { 121 | auto& cmp = compArray[i]; 122 | cmp.setBounds(BoundsF(x, y, w, cH).toNearestInt()); 123 | y += cH; 124 | } 125 | } 126 | 127 | BoundsF boundsOf(const Font&, const String&) noexcept; 128 | 129 | namespace imgPP 130 | { 131 | void blur(Image&, Graphics&, int /*its*/ = 3) noexcept; 132 | } 133 | } 134 | 135 | /* 136 | 137 | layout reduced by text width and/or height 138 | for example for tooltips 139 | 140 | */ -------------------------------------------------------------------------------- /Source/info.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ------------------------------------------------------------------- 4 | 5 | Welcome to DEFAULT PROJECT TEMPLATE! 6 | 7 | This project contains my vision of a default JUCE VST plugin project. I made this so I can get started trying new ideas 8 | and finishing projects way faster than from a projucer-made template. 9 | 10 | ------------------------------------------------------------------- 11 | 12 | HOW TO USE: 13 | 14 | 1. Copy the folder "Source" as well as the file "Project.jucer" into a new folder. 15 | 2. In Project.jucer 16 | 2.1 Rename plugin name 17 | 2.2 Define unique plugin ID 18 | 2.3 Configure the pre processor definitions 19 | 3. Code DSP in processBlockCustom and processBlockDownsampled 20 | 4. Code GUI in lowlevel.cpp/h 21 | 5. Write a new text for welcome.txt 22 | 5. Ship it! 23 | 24 | ------------------------------------------------------------------- 25 | 26 | TO DO: 27 | 28 | doesnt draw the stereo config button's text on init 29 | 30 | lock button over textbox of knob sometimes when interface smol 31 | 32 | rename ButtonState::UnityGain to ButtonState::Linked 33 | 34 | add layout function that paints a rectangle or something 35 | with a descriptive text to the topLeft 36 | 37 | scroll/draggable buttons must also have click function 38 | 39 | Formulaparser 40 | send toast on error 41 | wide when has focus 42 | randomize button should postfix>>infix and infix>>string 43 | 44 | EQPad 45 | can be confusing when multiple selected filters overlap after dragged out of bounds 46 | in decibels 47 | then with automatic range 48 | like [-3, 3]db or [-12, 12]db 49 | 50 | Manta 2 51 | am/rm between lanes 52 | visualize feedback of comb filter and heat? 53 | 54 | Parameter Randomizer Workflow 55 | Macro needs to be lockable too 56 | 57 | WaveTableDisplay 58 | spectral mode doesn't make sense yet 59 | 60 | Knob Looks 61 | make free funcs for knob vs slider behaviour 62 | for being called in makeParameter's Knob::Looks switch 63 | 64 | Reduce RAM-Usage 65 | Knob & Button 66 | make sort of a lookAndFeel thing, so that onPaints are not copied n times 67 | 68 | WaveTable 69 | implement spline editor 70 | 71 | tuningeditor 72 | button to play chromatic scale for preview 73 | 74 | Colours 75 | figure out natural hue steps 76 | if chosen white modulations not visible anymore 77 | some dark colours make it too dark 78 | 79 | highlevel 80 | "remove dc offset" switch (steep highpass) 81 | FIR or IIR? which freq? how steep? 82 | 83 | when saving a modpatch the preset browser needs to update its list too 84 | 85 | Pan Knob 86 | when value near 0 draw "C" and not "0" or "-0" 87 | 88 | SplineEditor 89 | Sometimes selection range shows wrong range 90 | and that feels bad 91 | Add Feature: Non-removable/Fixed Points 92 | Snap to Grid 93 | sometimes makes points on draggable 94 | make points come from outside architecturally (maybe?) 95 | wavetable from spline 96 | 97 | ScrollBar 98 | some things still not handled by scrollbarcomp alone 99 | enable scrolling not only on scrollbar hover 100 | 101 | PatchBrowser 102 | revisit tag system 103 | 104 | TextEditor 105 | if multiline text 106 | where does click put tick? 107 | 108 | sta::State 109 | rewrite with different types of state: 110 | per instance 111 | per plugin id 112 | per developer 113 | implement undo/redo 114 | 115 | MIDI Learn 116 | 1 cc to n parameters? 117 | (especially if multiple parameters are controlled by one knob/button) 118 | different midi channnels (16 * 128 CCs instead of 128) 119 | possible to save and load default state of whole midi learn patch 120 | ContextMenu 121 | implement context menu(s) for 122 | ccMonitor 123 | 124 | Label 125 | make free func(s) for grouping labels (n buttons) 126 | 127 | Options Menu 128 | how to make automatic updates 129 | 130 | All Params 131 | saving and loading max mod depths as preset 132 | 133 | Oversampler 134 | make less cpu demanding (FFT?) 135 | 136 | HighLevel UI Elements 137 | undo/redo buttons 138 | sidechain activated? 139 | 140 | ------------------------------------------------------------------- 141 | 142 | FEATURE REQUESTS: 143 | 144 | Manta 2 145 | autogain 146 | automatically finding kick's fundamental frequency 147 | keytrack 148 | 149 | show total time spent with mrugalla plugins 150 | 151 | Achievements System? 152 | discuss relevance, pros and cons 153 | 154 | */ -------------------------------------------------------------------------------- /Source/gui/PatchBrowser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TextEditor.h" 3 | #include "../arch/State.h" 4 | 5 | #define DebugNumPatches 0 6 | 7 | namespace gui 8 | { 9 | static constexpr int PatchNameWidth = 13; 10 | static constexpr int PatchAuthorWidth = 8; 11 | 12 | String getFileName(const String& name, const String& author); 13 | 14 | struct Patch : 15 | public Button 16 | { 17 | /* 18 | utils, name, author 19 | constructor for making temporary patches 20 | */ 21 | Patch(Utils&, const String&, const String&); 22 | 23 | /* 24 | utils, file 25 | constructor for reading patch from file */ 26 | Patch(Utils&, const File&); 27 | 28 | /* 29 | utils, name, author, valueTree 30 | constructor for saving patch from plugin state */ 31 | Patch(Utils&, const String&, const String&, const ValueTree&); 32 | 33 | bool operator==(const Patch&) const noexcept; 34 | 35 | bool operator!=(const Patch&) const noexcept; 36 | 37 | bool isRemovable() const; 38 | 39 | void resized() override; 40 | 41 | Label name, author; 42 | File file; 43 | 44 | protected: 45 | void init(); 46 | }; 47 | 48 | static constexpr float PatchRelHeight = 8.f; 49 | 50 | struct Patches : 51 | public CompScrollable 52 | { 53 | using UniquePatch = std::unique_ptr<Patch>; 54 | using SortFunc = std::function<bool(const UniquePatch&, const UniquePatch&)>; 55 | 56 | Patches(Utils&); 57 | 58 | int getIdx(const Patch&) const noexcept; 59 | 60 | bool contains(const Patch&) const noexcept; 61 | 62 | /* name, author */ 63 | bool save(const String&, const String&); 64 | 65 | bool add(const File&); 66 | 67 | bool removeSelected(); 68 | 69 | bool select(Patch*) noexcept; 70 | 71 | int getSelectedIdx() const noexcept; 72 | 73 | const Patch* getSelected() const noexcept; 74 | 75 | Patch& back() noexcept; 76 | 77 | const Patch& back() const noexcept; 78 | 79 | Patch& operator[](int) noexcept; 80 | 81 | const Patch& operator[](int) const noexcept; 82 | 83 | size_t numPatches() const noexcept; 84 | 85 | void sort(const SortFunc&); 86 | 87 | void resized() override; 88 | 89 | void applyFilters(const String&); 90 | 91 | void paint(Graphics&) override; 92 | 93 | void paintList(Graphics&); 94 | 95 | protected: 96 | std::vector<UniquePatch> patches; 97 | BoundsF listBounds; 98 | }; 99 | 100 | struct PatchesSortable : 101 | public Comp 102 | { 103 | using UniquePatch = Patches::UniquePatch; 104 | using SortFunc = Patches::SortFunc; 105 | 106 | PatchesSortable(Utils&); 107 | 108 | int getIdx(const Patch&) const noexcept; 109 | 110 | bool contains(const Patch&) const noexcept; 111 | 112 | /* name, author */ 113 | bool save(const String&, const String&); 114 | 115 | bool add(const File&); 116 | 117 | bool removeSelected(); 118 | 119 | bool select(Patch*) noexcept; 120 | 121 | int getSelectedIdx() const noexcept; 122 | 123 | const Patch* getSelected() const noexcept; 124 | 125 | Patch& back() noexcept; 126 | 127 | const Patch& back() const noexcept; 128 | 129 | Patch& operator[](int i) noexcept; 130 | 131 | const Patch& operator[](int i) const noexcept; 132 | 133 | size_t numPatches() const noexcept; 134 | 135 | void sort(const SortFunc&); 136 | 137 | void resized() override; 138 | 139 | void applyFilters(const String&); 140 | 141 | Patches& getPatches() noexcept; 142 | 143 | const Patches& getPatches() const noexcept; 144 | 145 | protected: 146 | Patches patches; 147 | Button sortByName, sortByAuthor; 148 | 149 | void paint(Graphics&) override; 150 | }; 151 | 152 | struct PatchBrowser : 153 | public CompScreenshotable 154 | { 155 | PatchBrowser(Utils&); 156 | 157 | void setVisible(bool) override; 158 | 159 | void paint(Graphics&) override; 160 | 161 | void resized() override; 162 | 163 | String getSelectedPatchName() const; 164 | 165 | protected: 166 | Button closeButton; 167 | Button saveButton, removeButton; 168 | 169 | PatchesSortable patches; 170 | 171 | TextEditor searchBar, authorEditor; 172 | 173 | void savePatch(); 174 | 175 | void removePatch(); 176 | 177 | void applyFilters(); 178 | 179 | void loadPatchesFromDisk(const File&); 180 | }; 181 | 182 | struct ButtonPatchBrowser : 183 | public Button 184 | { 185 | Notify makeNotify(ButtonPatchBrowser&); 186 | 187 | ButtonPatchBrowser(Utils&, PatchBrowser&); 188 | 189 | void paint(Graphics&) override; 190 | 191 | protected: 192 | PatchBrowser& browser; 193 | }; 194 | } -------------------------------------------------------------------------------- /Source/arch/Interpolation.cpp: -------------------------------------------------------------------------------- 1 | #include "Interpolation.h" 2 | 3 | namespace interpolate 4 | { 5 | template<typename T> 6 | T lerp(const T* samples, T idx, int size) noexcept 7 | { 8 | const auto iFloor = std::floor(idx); 9 | const auto x = idx - iFloor; 10 | 11 | const auto iF = static_cast<int>(iFloor); 12 | const auto a = samples[iF]; 13 | 14 | const auto iC = iF + 1; 15 | const auto b = iC != size ? samples[iC] : samples[0]; 16 | 17 | return a + x * (b - a); 18 | } 19 | 20 | template<typename T> 21 | T lerp(const T* samples, T idx) noexcept 22 | { 23 | const auto iFloor = std::floor(idx); 24 | const auto x = idx - iFloor; 25 | 26 | const auto iF = static_cast<int>(iFloor); 27 | const auto a = samples[iF]; 28 | 29 | const auto iC = iF + 1; 30 | const auto b = samples[iC]; 31 | 32 | return a + x * (b - a); 33 | } 34 | 35 | template<typename T> 36 | T cubicHermiteSpline(const T* buffer, T readHead, int size) noexcept 37 | { 38 | const auto iFloor = std::floor(readHead); 39 | auto i1 = static_cast<int>(iFloor); 40 | auto i0 = i1 - 1; 41 | auto i2 = i1 + 1; 42 | auto i3 = i1 + 2; 43 | if (i3 >= size) i3 -= size; 44 | if (i2 >= size) i2 -= size; 45 | if (i0 < 0) i0 += size; 46 | 47 | const auto t = readHead - iFloor; 48 | const auto v0 = buffer[i0]; 49 | const auto v1 = buffer[i1]; 50 | const auto v2 = buffer[i2]; 51 | const auto v3 = buffer[i3]; 52 | 53 | const auto c0 = v1; 54 | const auto c1 = static_cast<T>(.5) * (v2 - v0); 55 | const auto c2 = v0 - static_cast<T>(2.5) * v1 + static_cast<T>(2.) * v2 - static_cast<T>(.5) * v3; 56 | const auto c3 = static_cast<T>(1.5) * (v1 - v2) + static_cast<T>(.5) * (v3 - v0); 57 | 58 | return ((c3 * t + c2) * t + c1) * t + c0; 59 | } 60 | 61 | template<typename T> 62 | T cubicHermiteSpline(const T* buffer, T readHead) noexcept 63 | { 64 | const auto iFloor = std::floor(readHead); 65 | auto i0 = static_cast<int>(iFloor); 66 | auto i1 = i0 + 1; 67 | auto i2 = i0 + 2; 68 | auto i3 = i0 + 3; 69 | 70 | const auto t = readHead - iFloor; 71 | const auto v0 = buffer[i0]; 72 | const auto v1 = buffer[i1]; 73 | const auto v2 = buffer[i2]; 74 | const auto v3 = buffer[i3]; 75 | 76 | const auto c0 = v1; 77 | const auto c1 = static_cast<T>(.5) * (v2 - v0); 78 | const auto c2 = v0 - static_cast<T>(2.5) * v1 + static_cast<T>(2.) * v2 - static_cast<T>(.5) * v3; 79 | const auto c3 = static_cast<T>(1.5) * (v1 - v2) + static_cast<T>(.5) * (v3 - v0); 80 | 81 | return ((c3 * t + c2) * t + c1) * t + c0; 82 | } 83 | 84 | template float lerp<float>(const float*, float, int) noexcept; 85 | template double lerp<double>(const double* samples, double idx, int size) noexcept; 86 | template float lerp<float>(const float* samples, float idx) noexcept; 87 | template double lerp<double>(const double* samples, double idx) noexcept; 88 | template float cubicHermiteSpline<float>(const float* samples, float idx, int size) noexcept; 89 | template double cubicHermiteSpline<double>(const double* samples, double idx, int size) noexcept; 90 | template float cubicHermiteSpline<float>(const float* samples, float idx) noexcept; 91 | template double cubicHermiteSpline<double>(const double* samples, double idx) noexcept; 92 | 93 | /// 94 | 95 | namespace polynomial 96 | { 97 | template<typename Float> 98 | std::function<Float(Float)> getFunc(const std::vector<juce::Point<Float>>& points) 99 | { 100 | return [&pts = points](Float xp) 101 | { 102 | const auto n = pts.size(); 103 | auto yp = static_cast<Float>(0); 104 | 105 | { 106 | auto p = static_cast<Float>(1); 107 | 108 | for (auto j = 1; j < n; ++j) 109 | { 110 | const auto denom = pts[0].x - pts[j].x; 111 | if (denom != 0.f) 112 | p *= (xp - pts[j].x) / denom; 113 | } 114 | 115 | yp += p * pts[0].y; 116 | } 117 | 118 | for (auto i = 1; i < n; ++i) 119 | { 120 | auto p = static_cast<Float>(1); 121 | 122 | for (auto j = 0; j < n; ++j) 123 | if (i != j) 124 | { 125 | const auto denom = pts[i].x - pts[j].x; 126 | if (denom != 0.f) 127 | p *= (xp - pts[j].x) / denom; 128 | } 129 | 130 | 131 | yp += p * pts[i].y; 132 | } 133 | 134 | return yp; 135 | }; 136 | } 137 | 138 | template std::function<float(float)> getFunc<float>(const std::vector<juce::Point<float>>& points); 139 | template std::function<double(double)> getFunc<double>(const std::vector<juce::Point<double>>& points); 140 | } 141 | } -------------------------------------------------------------------------------- /Source/gui/FilterResponseGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "FilterResponseGraph.h" 2 | #include "../arch/Interpolation.h" 3 | 4 | namespace gui 5 | { 6 | //FilterResponseGraph 7 | 8 | FilterResponseGraph::FilterResponseGraph(Utils& u) : 9 | Comp(u, "", CursorType::Default), 10 | responseCurveCID(ColourID::Hover), 11 | processFilters(nullptr), 12 | needsUpdate(nullptr), 13 | 14 | impulse(), 15 | processBuffer(), 16 | dftBuffer(), 17 | 18 | responseCurve() 19 | { 20 | SIMD::clear(impulse.data(), Size); 21 | impulse[0] = 1.f; 22 | 23 | setInterceptsMouseClicks(false, false); 24 | startTimerHz(4); 25 | } 26 | 27 | void FilterResponseGraph::paint(Graphics& g) 28 | { 29 | if (responseCurve.isEmpty()) 30 | return; 31 | const auto thicc = utils.thicc; 32 | const Stroke stroke(thicc, Stroke::PathStrokeType::JointStyle::curved, Stroke::PathStrokeType::EndCapStyle::rounded); 33 | 34 | g.setColour(Colours::c(responseCurveCID)); 35 | g.strokePath(responseCurve, stroke); 36 | } 37 | 38 | void FilterResponseGraph::resized() 39 | { 40 | if (responseCurve.isEmpty()) 41 | return; 42 | 43 | updateResponseCurve(); 44 | } 45 | 46 | void FilterResponseGraph::timerCallback() 47 | { 48 | if (needsUpdate()) 49 | { 50 | updateResponseCurve(); 51 | repaint(); 52 | } 53 | } 54 | 55 | void FilterResponseGraph::updateResponseCurve() 56 | { 57 | resetBuffers(); 58 | 59 | processFilters(processBuffer.data(), impulse, Size); 60 | 61 | applyForwardDFT(); 62 | 63 | generateResponseCurve(); 64 | } 65 | 66 | void FilterResponseGraph::resetBuffers() 67 | { 68 | SIMD::clear(processBuffer.data(), processBuffer.size()); 69 | 70 | for (auto& d : dftBuffer) 71 | d = { 0.f, 0.f }; 72 | } 73 | 74 | void FilterResponseGraph::applyForwardDFT() 75 | { 76 | auto buf = processBuffer.data(); 77 | auto dft = dftBuffer.data(); 78 | 79 | for (auto n = 0; n < Size; ++n) 80 | { 81 | const auto f = Pi * static_cast<float>(n) * SizeInv; 82 | auto real = 0.f; 83 | auto imag = 0.f; 84 | for (auto s = 0; s < Size; ++s) 85 | { 86 | const auto sf = s * f; 87 | real += buf[s] * std::cos(sf); 88 | imag -= buf[s] * std::sin(sf); 89 | } 90 | dft[n] += std::complex<float>{ real, imag }; 91 | } 92 | 93 | for (auto i = 0; i < Size; ++i) 94 | buf[i] = std::abs(dft[i]); 95 | } 96 | 97 | void FilterResponseGraph::generateResponseCurve() 98 | { 99 | const auto thicc = utils.thicc; 100 | const auto w = static_cast<float>(getWidth()); 101 | const auto h = static_cast<float>(getHeight()); 102 | const auto wInv = 1.f / w; 103 | const auto buf = processBuffer.data(); 104 | const auto& xen = utils.audioProcessor.xenManager; 105 | const auto fsInv = 1.f / static_cast<float>(utils.audioProcessor.getSampleRate()); 106 | 107 | responseCurve.clear(); 108 | { 109 | const auto y = h - h * buf[0]; 110 | responseCurve.startNewSubPath(0.f, y); 111 | } 112 | for (auto x = thicc; x < w; x += 1.f) 113 | { 114 | const auto r = x * wInv; 115 | const auto pitch = r * 128.f; 116 | const auto freqHz = xen.noteToFreqHzWithWrap(pitch + xen.getXen()); 117 | const auto fc = freqHz * fsInv; 118 | const auto idx = fc * SizeF; 119 | 120 | auto y = h - h * interpolate::lerp(buf, idx); 121 | y = juce::jlimit(0.f, h, y); 122 | 123 | responseCurve.lineTo(x, y); 124 | } 125 | responseCurve.lineTo(w, h - h * buf[Size - 1]); 126 | } 127 | 128 | //FilterResponseGraph2 129 | 130 | FilterResponseGraph2::FilterResponseGraph2(Utils& u) : 131 | Comp(u, "", CursorType::Default), 132 | responseCurveCID(ColourID::Hover), 133 | shallUpdate(), 134 | update(), 135 | responseCurve() 136 | { 137 | setInterceptsMouseClicks(false, false); 138 | startTimerHz(PPDFPSKnobs); 139 | } 140 | 141 | void FilterResponseGraph2::paint(Graphics& g) 142 | { 143 | if (responseCurve.isEmpty()) 144 | return; 145 | const auto thicc = utils.thicc; 146 | const Stroke stroke(thicc, Stroke::PathStrokeType::JointStyle::curved, Stroke::PathStrokeType::EndCapStyle::rounded); 147 | 148 | g.setColour(Colours::c(responseCurveCID)); 149 | g.strokePath(responseCurve, stroke); 150 | } 151 | 152 | void FilterResponseGraph2::timerCallback() 153 | { 154 | if (shallUpdate()) 155 | resized(); 156 | } 157 | 158 | void FilterResponseGraph2::resized() 159 | { 160 | const auto w = static_cast<float>(getWidth()); 161 | const auto h = static_cast<float>(getHeight()); 162 | 163 | update(responseCurve, w, h); 164 | repaint(); 165 | } 166 | } -------------------------------------------------------------------------------- /Source/fonts/Dosis/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2011 The Dosis Project Authors (https://github.com/googlefonts/dosis-vf) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /Source/fonts/Ms_Madi/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 The Ms Madi Project Authors (https://github.com/googlefonts/ms-madi) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /Source/fonts/Lobster/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010 The Lobster Project Authors (https://github.com/impallari/The-Lobster-Font), with Reserved Font Name "Lobster". 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /Source/audio/Manta.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Filter.h" 4 | #include "PRM.h" 5 | #include "Phasor.h" 6 | #include "WaveTable.h" 7 | #include "XenManager.h" 8 | #include "WHead.h" 9 | 10 | namespace audio 11 | { 12 | struct Manta 13 | { 14 | // enabled, pitch-snap, cutoff, resonance, slope, feedback, oct, semi, heat, rm-oct, rm-semi, rm-depth, gain 15 | static constexpr int NumParametersPerLane = 13; 16 | static constexpr int WaveTableSize = 1 << 13; // around min 5hz 17 | static constexpr int NumLanes = 3; 18 | static constexpr int MaxSlopeStage = 4; //4*12db/oct 19 | using WT = WaveTable<WaveTableSize>; 20 | private: 21 | class Filter 22 | { 23 | using Fltr = FilterBandpassSlope<MaxSlopeStage>; 24 | public: 25 | Filter(); 26 | 27 | /* laneBuf, samples, numChannels, numSamples, fcBuf, resoBuf, stage */ 28 | void operator()(float* const*, float* const*, int, int, 29 | float*, float*, int) noexcept; 30 | 31 | protected: 32 | std::array<Fltr, 2> filta; 33 | }; 34 | 35 | struct DelayFeedback 36 | { 37 | DelayFeedback(); 38 | 39 | /* delaySize */ 40 | void prepare(int); 41 | 42 | /* samples, numChannels, numSamples, wHead, rHead, feedback */ 43 | void operator()(float* const*, int, int, const int*, const float*, const float*) noexcept; 44 | 45 | AudioBuffer ringBuffer; 46 | int size; 47 | }; 48 | 49 | struct RingMod 50 | { 51 | using WTFunc = WT::Func; 52 | 53 | RingMod(); 54 | 55 | void createWavetable(const WTFunc&) noexcept; 56 | 57 | /* Fs, blockSize */ 58 | void prepare(float, int); 59 | 60 | /* samples, numChannels, numSamples, rmDepth, freqHz */ 61 | void operator()(float* const*, int, int, float*, float*) noexcept; 62 | 63 | WT waveTable; 64 | protected: 65 | Phasor<float> phasor; 66 | std::vector<float> oscBuffer; 67 | }; 68 | 69 | struct Lane 70 | { 71 | Lane(); 72 | 73 | /* sampleRate, blockSize, delaySize */ 74 | void prepare(float, int, int); 75 | 76 | /* samples, numChannels, numSamples, enabled, pitch, resonance, slope, drive, feedback, 77 | oct, semi, rmOct, rmSemi, rmDepth, gain, wHead, xen */ 78 | void operator()(float* const*, int, int, bool, float, float, int, float, float, 79 | float, float, float, float, float, float, const int*, const XenManager&) noexcept; 80 | 81 | /* state, laneIndex */ 82 | void savePatch(sta::State&, int); 83 | 84 | /* state, laneIndex */ 85 | void loadPatch(sta::State&, int); 86 | 87 | /* samples, numChannels, numSamples */ 88 | void addTo(float* const*, int, int) noexcept; 89 | 90 | RingMod ringMod; 91 | protected: 92 | AudioBuffer laneBuffer; 93 | std::vector<float> readHead; 94 | Filter filter; 95 | PRM frequency, resonance, drive, feedback, delayRate, rmDepth, rmFreqHz, gain; 96 | DelayFeedback delayFB; 97 | float Fs, delaySizeF; 98 | 99 | /* numSamples, wHead, delayBuf */ 100 | const float* getRHead(int, const int*, const float*) noexcept; 101 | 102 | /* x, d */ 103 | float distort(float, float) const noexcept; 104 | 105 | /* samples, numChannels, numSamples, driveBuf */ 106 | void distort(float* const*, int, int, const float*) noexcept; 107 | 108 | /* samples, numChannels, numSamples, gainBuf */ 109 | void applyGain(float* const*, int, int, const float*) noexcept; 110 | }; 111 | 112 | public: 113 | Manta(const XenManager&); 114 | 115 | /* sampleRate, blockSize */ 116 | void prepare(float, int); 117 | 118 | /* samples, numChannels, numSamples, 119 | * l1Enabled [0, 1], l1Snap, l1Pitch [12, N]note, l1Resonance [1, N]q, l1Slope [1, 4]db/oct, l1Drive [0, 1]%, l1Feedback [0, 1]%, l1Oct[-3,3], l1Semi[-12,12], l1RMOct[-3,3], l1RMSemi[-12,12], l1RMDepth[0,1], l1Gain [-60, 60]db 120 | * l2Enabled [0, 1], l2Snap, l2Pitch [12, N]note, l2Resonance [1, N]q, l2Slope [1, 4]db/oct, l2Drive [0, 1]%, l2Feedback [0, 1]%, l2Oct[-3,3], l2Semi[-12,12], l2RMOct[-3,3], l2RMSemi[-12,12], l2RMDepth[0,1], l2Gain [-60, 60]db 121 | * l3Enabled [0, 1], l3Snap, l3Pitch [12, N]note, l3Resonance [1, N]q, l3Slope [1, 4]db/oct, l3Drive [0, 1]%, l3Feedback [0, 1]%, l3Oct[-3,3], l3Semi[-12,12], l3RMOct[-3,3], l3RMSemi[-12,12], l3RMDepth[0,1], l3Gain [-60, 60]db 122 | */ 123 | void operator()(float* const*, int, int, 124 | bool, bool, float, float, int, float, float, float, float, float, float, float, float, 125 | bool, bool, float, float, int, float, float, float, float, float, float, float, float, 126 | bool, bool, float, float, int, float, float, float, float, float, float, float, float) noexcept; 127 | 128 | void savePatch(sta::State&); 129 | 130 | void loadPatch(sta::State&); 131 | 132 | /* laneIdx */ 133 | WT& getWaveTable(int) noexcept; 134 | 135 | /* laneIdx */ 136 | const WT& getWaveTable(int) const noexcept; 137 | 138 | protected: 139 | const XenManager& xen; 140 | std::array<Lane, NumLanes> lanes; 141 | WHead writeHead; 142 | public: 143 | int delaySize; 144 | }; 145 | } 146 | 147 | /* 148 | 149 | */ -------------------------------------------------------------------------------- /Source/gui/GUIParams.cpp: -------------------------------------------------------------------------------- 1 | #include "GUIParams.h" 2 | 3 | namespace gui 4 | { 5 | Parametr::ModDial::ModDial(Utils& u, Parametr& _parametr) : 6 | Comp(u, "Drag this to modulate the macro's modulation depth.", CursorType::Mod), 7 | parametr(_parametr), 8 | label(u, "M"), 9 | dragY(0.f), 10 | state(State::MaxModDepth) 11 | { 12 | label.mode = Label::Mode::TextToLabelBounds; 13 | label.textCID = ColourID::Bg; 14 | addAndMakeVisible(label); 15 | } 16 | 17 | void Parametr::ModDial::paint(Graphics& g) 18 | { 19 | const auto bounds = getLocalBounds().toFloat(); 20 | Colour col; 21 | switch (state) 22 | { 23 | case State::MaxModDepth: 24 | col = Colours::c(ColourID::Mod); 25 | break; 26 | case State::Bias: 27 | col = Colours::c(ColourID::Bias); 28 | break; 29 | } 30 | g.setColour(col); 31 | g.fillEllipse(bounds); 32 | } 33 | 34 | Parametr::ModDial::State Parametr::ModDial::getState() const noexcept { return state; } 35 | 36 | void Parametr::ModDial::resized() 37 | { 38 | const auto thicc = utils.thicc; 39 | label.setBounds(getLocalBounds().toFloat().reduced(thicc * .5f).toNearestInt()); 40 | } 41 | 42 | void Parametr::ModDial::mouseDown(const Mouse& mouse) 43 | { 44 | switch (state) 45 | { 46 | case State::MaxModDepth: 47 | dragY = mouse.position.y / utils.getDragSpeed(); 48 | break; 49 | case State::Bias: 50 | dragY = mouse.position.y / utils.getDragSpeed() * 2.f; 51 | break; 52 | } 53 | } 54 | 55 | void Parametr::ModDial::mouseDrag(const Mouse& mouse) 56 | { 57 | if (mouse.mods.isLeftButtonDown()) 58 | { 59 | float dragYNew; 60 | switch (state) 61 | { 62 | case State::MaxModDepth: 63 | 64 | dragYNew = mouse.position.y / utils.getDragSpeed(); 65 | break; 66 | case State::Bias: 67 | dragYNew = mouse.position.y / utils.getDragSpeed() * 2.f; 68 | break; 69 | default: 70 | dragYNew = 0.f; 71 | break; 72 | } 73 | auto dragOffset = dragYNew - dragY; 74 | if (mouse.mods.isShiftDown()) 75 | dragOffset *= SensitiveDrag; 76 | float newValue; 77 | switch (state) 78 | { 79 | case State::MaxModDepth: 80 | newValue = parametr.param.getMaxModDepth() - dragOffset; 81 | parametr.param.setMaxModDepth(newValue); 82 | break; 83 | case State::Bias: 84 | newValue = parametr.param.getModBias() - dragOffset; 85 | parametr.param.setModBias(newValue); 86 | break; 87 | } 88 | 89 | dragY = dragYNew; 90 | } 91 | } 92 | 93 | void Parametr::ModDial::mouseUp(const Mouse& mouse) 94 | { 95 | if (!mouse.mouseWasDraggedSinceMouseDown()) 96 | if (mouse.mods.isCtrlDown()) 97 | { 98 | switch (state) 99 | { 100 | case State::MaxModDepth: 101 | 102 | parametr.param.setMaxModDepth(0.f); 103 | break; 104 | case State::Bias: 105 | parametr.param.setModBias(.5f); 106 | break; 107 | } 108 | } 109 | else if (mouse.mods.isRightButtonDown()) 110 | { 111 | state = static_cast<State>((static_cast<int>(state) + 1) % NumStates); 112 | switch (state) 113 | { 114 | case State::MaxModDepth: 115 | label.setText("M"); 116 | setCursorType(CursorType::Mod); 117 | break; 118 | case State::Bias: 119 | label.setText("B"); 120 | setCursorType(CursorType::Bias); 121 | break; 122 | } 123 | repaintWithChildren(¶metr); 124 | } 125 | } 126 | 127 | Parametr::Parametr(Utils& u, PID _pID, bool _modulatable) : 128 | Comp(u, param::toTooltip(_pID)), 129 | param(*u.getParam(_pID)), 130 | modDial(u, *this), 131 | valNorm(param.getValue()), 132 | maxModDepth(param.getMaxModDepth()), 133 | valMod(param.getValMod()), 134 | modBias(param.getModBias()), 135 | locked(param.isLocked()), 136 | modulatable(_modulatable) 137 | { 138 | if (modulatable) 139 | addAndMakeVisible(modDial); 140 | 141 | setLocked(param.isLocked()); 142 | } 143 | 144 | PID Parametr::getPID() const noexcept { return param.id; } 145 | 146 | void Parametr::setLocked(bool lckd) 147 | { 148 | locked = lckd; 149 | if (locked) 150 | { 151 | setCursorType(CursorType::Inactive); 152 | setAlpha(.2f); 153 | } 154 | else 155 | { 156 | setCursorType(CursorType::Interact); 157 | setAlpha(1.f); 158 | } 159 | } 160 | } 161 | 162 | --------------------------------------------------------------------------------